diff options
Diffstat (limited to 'drivers')
58 files changed, 5855 insertions, 557 deletions
| diff --git a/drivers/block/mvsata_ide.c b/drivers/block/mvsata_ide.c index a88d0f7f8..42c177fe4 100644 --- a/drivers/block/mvsata_ide.c +++ b/drivers/block/mvsata_ide.c @@ -56,8 +56,8 @@ struct mvsata_port_registers {   * Sanity checks:   * - to compile at all, we need CONFIG_SYS_ATA_BASE_ADDR.   * - for ide_preinit to make sense, we need at least one of - *   CONFIG_SYS_ATA_IDE0_OFFSET or CONFIG_SYS_ATA_IDE0_OFFSET; - * - for inde_preinit to be called, we need CONFIG_IDE_PREINIT. + *   CONFIG_SYS_ATA_IDE0_OFFSET or CONFIG_SYS_ATA_IDE1_OFFSET; + * - for ide_preinit to be called, we need CONFIG_IDE_PREINIT.   * Fail with an explanation message if these conditions are not met.   * This is particularly important for CONFIG_IDE_PREINIT, because   * its lack would not cause a build error. diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c index 5d504dffd..083d74591 100644 --- a/drivers/dfu/dfu_mmc.c +++ b/drivers/dfu/dfu_mmc.c @@ -21,6 +21,7 @@  #include <common.h>  #include <malloc.h> +#include <errno.h>  #include <dfu.h>  enum dfu_mmc_op { @@ -153,6 +154,10 @@ int dfu_read_medium_mmc(struct dfu_entity *dfu, void *buf, long *len)  int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)  { +	int dev, part; +	struct mmc *mmc; +	block_dev_desc_t *blk_dev; +	disk_partition_t partinfo;  	char *st;  	dfu->dev_type = DFU_DEV_MMC; @@ -166,8 +171,34 @@ int dfu_fill_entity_mmc(struct dfu_entity *dfu, char *s)  		dfu->layout = DFU_FS_FAT;  	} else if (!strcmp(st, "ext4")) {  		dfu->layout = DFU_FS_EXT4; +	} else if (!strcmp(st, "part")) { + +		dfu->layout = DFU_RAW_ADDR; + +		dev = simple_strtoul(s, &s, 10); +		s++; +		part = simple_strtoul(s, &s, 10); + +		mmc = find_mmc_device(dev); +		if (mmc == NULL || mmc_init(mmc)) { +			printf("%s: could not find mmc device #%d!\n", __func__, dev); +			return -ENODEV; +		} + +		blk_dev = &mmc->block_dev; +		if (get_partition_info(blk_dev, part, &partinfo) != 0) { +			printf("%s: could not find partition #%d on mmc device #%d!\n", +					__func__, part, dev); +			return -ENODEV; +		} + +		dfu->data.mmc.lba_start = partinfo.start; +		dfu->data.mmc.lba_size = partinfo.size; +		dfu->data.mmc.lba_blk_size = partinfo.blksz; +  	} else {  		printf("%s: Memory layout (%s) not supported!\n", __func__, st); +		return -ENODEV;  	}  	if (dfu->layout == DFU_FS_EXT4 || dfu->layout == DFU_FS_FAT) { diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 2d97b4f1e..9df1e2632 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -39,6 +39,7 @@ COBJS-$(CONFIG_SANDBOX_GPIO)	+= sandbox.o  COBJS-$(CONFIG_SPEAR_GPIO)	+= spear_gpio.o  COBJS-$(CONFIG_TEGRA_GPIO)	+= tegra_gpio.o  COBJS-$(CONFIG_DA8XX_GPIO)	+= da8xx_gpio.o +COBJS-$(CONFIG_DM644X_GPIO)	+= da8xx_gpio.o  COBJS-$(CONFIG_ALTERA_PIO)	+= altera_pio.o  COBJS-$(CONFIG_MPC83XX_GPIO)	+= mpc83xx_gpio.o  COBJS-$(CONFIG_SH_GPIO_PFC)	+= sh_pfc.o diff --git a/drivers/gpio/da8xx_gpio.c b/drivers/gpio/da8xx_gpio.c index 84d2b77d9..76648d27d 100644 --- a/drivers/gpio/da8xx_gpio.c +++ b/drivers/gpio/da8xx_gpio.c @@ -31,8 +31,141 @@ static struct gpio_registry {  	char name[GPIO_NAME_SIZE];  } gpio_registry[MAX_NUM_GPIOS]; +#if defined(CONFIG_SOC_DA8XX)  #define pinmux(x)       (&davinci_syscfg_regs->pinmux[x]) +#if defined(CONFIG_SOC_DA8XX) && !defined(CONFIG_SOC_DA850) +static const struct pinmux_config gpio_pinmux[] = { +	{ pinmux(13), 8, 6 },	/* GP0[0] */ +	{ pinmux(13), 8, 7 }, +	{ pinmux(14), 8, 0 }, +	{ pinmux(14), 8, 1 }, +	{ pinmux(14), 8, 2 }, +	{ pinmux(14), 8, 3 }, +	{ pinmux(14), 8, 4 }, +	{ pinmux(14), 8, 5 }, +	{ pinmux(14), 8, 6 }, +	{ pinmux(14), 8, 7 }, +	{ pinmux(15), 8, 0 }, +	{ pinmux(15), 8, 1 }, +	{ pinmux(15), 8, 2 }, +	{ pinmux(15), 8, 3 }, +	{ pinmux(15), 8, 4 }, +	{ pinmux(15), 8, 5 }, +	{ pinmux(15), 8, 6 },	/* GP1[0] */ +	{ pinmux(15), 8, 7 }, +	{ pinmux(16), 8, 0 }, +	{ pinmux(16), 8, 1 }, +	{ pinmux(16), 8, 2 }, +	{ pinmux(16), 8, 3 }, +	{ pinmux(16), 8, 4 }, +	{ pinmux(16), 8, 5 }, +	{ pinmux(16), 8, 6 }, +	{ pinmux(16), 8, 7 }, +	{ pinmux(17), 8, 0 }, +	{ pinmux(17), 8, 1 }, +	{ pinmux(17), 8, 2 }, +	{ pinmux(17), 8, 3 }, +	{ pinmux(17), 8, 4 }, +	{ pinmux(17), 8, 5 }, +	{ pinmux(17), 8, 6 },	/* GP2[0] */ +	{ pinmux(17), 8, 7 }, +	{ pinmux(18), 8, 0 }, +	{ pinmux(18), 8, 1 }, +	{ pinmux(18), 8, 2 }, +	{ pinmux(18), 8, 3 }, +	{ pinmux(18), 8, 4 }, +	{ pinmux(18), 8, 5 }, +	{ pinmux(18), 8, 6 }, +	{ pinmux(18), 8, 7 }, +	{ pinmux(19), 8, 0 }, +	{ pinmux(9), 8, 2 }, +	{ pinmux(9), 8, 3 }, +	{ pinmux(9), 8, 4 }, +	{ pinmux(9), 8, 5 }, +	{ pinmux(9), 8, 6 }, +	{ pinmux(10), 8, 1 },	/* GP3[0] */ +	{ pinmux(10), 8, 2 }, +	{ pinmux(10), 8, 3 }, +	{ pinmux(10), 8, 4 }, +	{ pinmux(10), 8, 5 }, +	{ pinmux(10), 8, 6 }, +	{ pinmux(10), 8, 7 }, +	{ pinmux(11), 8, 0 }, +	{ pinmux(11), 8, 1 }, +	{ pinmux(11), 8, 2 }, +	{ pinmux(11), 8, 3 }, +	{ pinmux(11), 8, 4 }, +	{ pinmux(9), 8, 7 }, +	{ pinmux(2), 8, 6 }, +	{ pinmux(11), 8, 5 }, +	{ pinmux(11), 8, 6 }, +	{ pinmux(12), 8, 4 },	/* GP4[0] */ +	{ pinmux(12), 8, 5 }, +	{ pinmux(12), 8, 6 }, +	{ pinmux(12), 8, 7 }, +	{ pinmux(13), 8, 0 }, +	{ pinmux(13), 8, 1 }, +	{ pinmux(13), 8, 2 }, +	{ pinmux(13), 8, 3 }, +	{ pinmux(13), 8, 4 }, +	{ pinmux(13), 8, 5 }, +	{ pinmux(11), 8, 7 }, +	{ pinmux(12), 8, 0 }, +	{ pinmux(12), 8, 1 }, +	{ pinmux(12), 8, 2 }, +	{ pinmux(12), 8, 3 }, +	{ pinmux(9), 8, 1 }, +	{ pinmux(7), 8, 3 },	/* GP5[0] */ +	{ pinmux(7), 8, 4 }, +	{ pinmux(7), 8, 5 }, +	{ pinmux(7), 8, 6 }, +	{ pinmux(7), 8, 7 }, +	{ pinmux(8), 8, 0 }, +	{ pinmux(8), 8, 1 }, +	{ pinmux(8), 8, 2 }, +	{ pinmux(8), 8, 3 }, +	{ pinmux(8), 8, 4 }, +	{ pinmux(8), 8, 5 }, +	{ pinmux(8), 8, 6 }, +	{ pinmux(8), 8, 7 }, +	{ pinmux(9), 8, 0 }, +	{ pinmux(7), 8, 1 }, +	{ pinmux(7), 8, 2 }, +	{ pinmux(5), 8, 1 },	/* GP6[0] */ +	{ pinmux(5), 8, 2 }, +	{ pinmux(5), 8, 3 }, +	{ pinmux(5), 8, 4 }, +	{ pinmux(5), 8, 5 }, +	{ pinmux(5), 8, 6 }, +	{ pinmux(5), 8, 7 }, +	{ pinmux(6), 8, 0 }, +	{ pinmux(6), 8, 1 }, +	{ pinmux(6), 8, 2 }, +	{ pinmux(6), 8, 3 }, +	{ pinmux(6), 8, 4 }, +	{ pinmux(6), 8, 5 }, +	{ pinmux(6), 8, 6 }, +	{ pinmux(6), 8, 7 }, +	{ pinmux(7), 8, 0 }, +	{ pinmux(1), 8, 0 },	/* GP7[0] */ +	{ pinmux(1), 8, 1 }, +	{ pinmux(1), 8, 2 }, +	{ pinmux(1), 8, 3 }, +	{ pinmux(1), 8, 4 }, +	{ pinmux(1), 8, 5 }, +	{ pinmux(1), 8, 6 }, +	{ pinmux(1), 8, 7 }, +	{ pinmux(2), 8, 0 }, +	{ pinmux(2), 8, 1 }, +	{ pinmux(2), 8, 2 }, +	{ pinmux(2), 8, 3 }, +	{ pinmux(2), 8, 4 }, +	{ pinmux(2), 8, 5 }, +	{ pinmux(0), 1, 0 }, +	{ pinmux(0), 1, 1 }, +}; +#else /* CONFIG_SOC_DA8XX && CONFIG_SOC_DA850 */  static const struct pinmux_config gpio_pinmux[] = {  	{ pinmux(1), 8, 7 },	/* GP0[0] */  	{ pinmux(1), 8, 6 }, @@ -179,6 +312,10 @@ static const struct pinmux_config gpio_pinmux[] = {  	{ pinmux(18), 8, 3 },  	{ pinmux(18), 8, 2 },  }; +#endif /* CONFIG_SOC_DA8XX && !CONFIG_SOC_DA850 */ +#else /* !CONFIG_SOC_DA8XX */ +#define davinci_configure_pin_mux(a, b) +#endif /* CONFIG_SOC_DA8XX */  int gpio_request(unsigned gpio, const char *label)  { diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index 769a2ba5b..46d25061e 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -86,13 +86,6 @@ static int GetI2CSDA(void)  #endif  } -#if 0 -static void SetI2CSDA(int x) -{ -	rGPEDAT = (rGPEDAT & ~0x8000) | (x & 1) << 15; -} -#endif -  static void SetI2CSCL(int x)  {  	struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); @@ -331,7 +324,7 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,  			writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,  			       &i2c->iicstat);  			i = 0; -			while ((i < data_len) && (result = I2C_OK)) { +			while ((i < data_len) && (result == I2C_OK)) {  				result = WaitForXfer(i2c);  				writel(data[i], &i2c->iicds);  				ReadWriteByte(i2c); @@ -343,17 +336,16 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,  			result = WaitForXfer(i2c);  		/* send STOP */ -		writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); +		writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);  		ReadWriteByte(i2c);  		break;  	case I2C_READ:  		if (addr && addr_len) { -			writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);  			writel(chip, &i2c->iicds);  			/* send START */ -			writel(readl(&i2c->iicstat) | I2C_START_STOP, -			       &i2c->iicstat); +			writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, +				&i2c->iicstat);  			result = WaitForXfer(i2c);  			if (IsACK(i2c)) {  				i = 0; @@ -387,11 +379,10 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,  			}  		} else { -			writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);  			writel(chip, &i2c->iicds);  			/* send START */ -			writel(readl(&i2c->iicstat) | I2C_START_STOP, -			       &i2c->iicstat); +			writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP, +				&i2c->iicstat);  			result = WaitForXfer(i2c);  			if (IsACK(i2c)) { diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index efc77fa91..ca71cd3ee 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -46,6 +46,7 @@ struct i2c_bus {  	struct i2c_control	*control;  	struct i2c_ctlr		*regs;  	int			is_dvc;	/* DVC type, rather than I2C */ +	int			is_scs;	/* single clock source (T114+) */  	int			inited;	/* bus is inited */  }; @@ -88,7 +89,28 @@ static void i2c_init_controller(struct i2c_bus *i2c_bus)  	 * 16 to get the right frequency.  	 */  	clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, -			       i2c_bus->speed * 2 * 8); +		i2c_bus->speed * 2 * 8); + +	if (i2c_bus->is_scs) { +		/* +		 * T114 I2C went to a single clock source for standard/fast and +		 * HS clock speeds. The new clock rate setting calculation is: +		 *  SCL = CLK_SOURCE.I2C / +		 *   (CLK_MULT_STD_FAST_MODE * (I2C_CLK_DIV_STD_FAST_MODE+1) * +		 *   I2C FREQUENCY DIVISOR) as per the T114 TRM (sec 30.3.1). +		 * +		 * NOTE: We do this here, after the initial clock/pll start, +		 * because if we read the clk_div reg before the controller +		 * is running, we hang, and we need it for the new calc. +		 */ +		int clk_div_stdfst_mode = readl(&i2c_bus->regs->clk_div) >> 16; +		debug("%s: CLK_DIV_STD_FAST_MODE setting = %d\n", __func__, +			clk_div_stdfst_mode); + +		clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, +			CLK_MULT_STD_FAST_MODE * (clk_div_stdfst_mode + 1) * +			i2c_bus->speed * 2); +	}  	/* Reset I2C controller. */  	i2c_reset_controller(i2c_bus); @@ -352,10 +374,11 @@ static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus)   * @param node_list	list of nodes to process (any <=0 are ignored)   * @param count		number of nodes to process   * @param is_dvc	1 if these are DVC ports, 0 if standard I2C + * @param is_scs	1 if this HW uses a single clock source (T114+)   * @return 0 if ok, -1 on error   */  static int process_nodes(const void *blob, int node_list[], int count, -			 int is_dvc) +			 int is_dvc, int is_scs)  {  	struct i2c_bus *i2c_bus;  	int i; @@ -375,6 +398,8 @@ static int process_nodes(const void *blob, int node_list[], int count,  			return -1;  		} +		i2c_bus->is_scs = is_scs; +  		i2c_bus->is_dvc = is_dvc;  		if (is_dvc) {  			i2c_bus->control = @@ -403,18 +428,25 @@ void i2c_init_board(void)  	const void *blob = gd->fdt_blob;  	int count; -	/* First get the normal i2c ports */ +	/* First check for newer (T114+) I2C ports */ +	count = fdtdec_find_aliases_for_id(blob, "i2c", +			COMPAT_NVIDIA_TEGRA114_I2C, node_list, +			TEGRA_I2C_NUM_CONTROLLERS); +	if (process_nodes(blob, node_list, count, 0, 1)) +		return; + +	/* Now get the older (T20/T30) normal I2C ports */  	count = fdtdec_find_aliases_for_id(blob, "i2c",  			COMPAT_NVIDIA_TEGRA20_I2C, node_list,  			TEGRA_I2C_NUM_CONTROLLERS); -	if (process_nodes(blob, node_list, count, 0)) +	if (process_nodes(blob, node_list, count, 0, 0))  		return;  	/* Now look for dvc ports */  	count = fdtdec_add_aliases_for_id(blob, "i2c",  			COMPAT_NVIDIA_TEGRA20_DVC, node_list,  			TEGRA_I2C_NUM_CONTROLLERS); -	if (process_nodes(blob, node_list, count, 1)) +	if (process_nodes(blob, node_list, count, 1, 0))  		return;  } diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 65791aa21..1d6faa2a9 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -43,6 +43,7 @@ COBJS-$(CONFIG_MXS_MMC) += mxsmmc.o  COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o  COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o  COBJS-$(CONFIG_SDHCI) += sdhci.o +COBJS-$(CONFIG_BCM2835_SDHCI) += bcm2835_sdhci.o  COBJS-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o  COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o  COBJS-$(CONFIG_TEGRA_MMC) += tegra_mmc.o diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c index af1380a45..ab2e81e5d 100644 --- a/drivers/mmc/arm_pl180_mmci.c +++ b/drivers/mmc/arm_pl180_mmci.c @@ -377,6 +377,7 @@ int arm_pl180_mmci_init(struct pl180_mmc_host *host)  	dev->set_ios = host_set_ios;  	dev->init = mmc_host_reset;  	dev->getcd = NULL; +	dev->getwp = NULL;  	dev->host_caps = host->caps;  	dev->voltages = host->voltages;  	dev->f_min = host->clock_min; diff --git a/drivers/mmc/bcm2835_sdhci.c b/drivers/mmc/bcm2835_sdhci.c new file mode 100644 index 000000000..b0afc3c9c --- /dev/null +++ b/drivers/mmc/bcm2835_sdhci.c @@ -0,0 +1,189 @@ +/* + * This code was extracted from: + * git://github.com/gonzoua/u-boot-pi.git master + * and hence presumably (C) 2012 Oleksandr Tymoshenko + * + * Tweaks for U-Boot upstreaming + * (C) 2012 Stephen Warren + * + * Portions (e.g. read/write macros, concepts for back-to-back register write + * timing workarounds) obviously extracted from the Linux kernel at: + * https://github.com/raspberrypi/linux.git rpi-3.6.y + * + * The Linux kernel code has the following (c) and license, which is hence + * propagated to Oleksandr's tree and here: + * + * Support for SDHCI device on 2835 + * Based on sdhci-bcm2708.c (c) 2010 Broadcom + * + * 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. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * SDHCI platform device - Arasan SD controller in BCM2708 + * + * Inspired by sdhci-pci.c, by Pierre Ossman + */ + +#include <common.h> +#include <malloc.h> +#include <sdhci.h> + +/* 400KHz is max freq for card ID etc. Use that as min */ +#define MIN_FREQ 400000 + +struct bcm2835_sdhci_host { +	struct sdhci_host host; +	uint twoticks_delay; +	ulong last_write; +}; + +static inline struct bcm2835_sdhci_host *to_bcm(struct sdhci_host *host) +{ +	return (struct bcm2835_sdhci_host *)host; +} + +static inline void bcm2835_sdhci_raw_writel(struct sdhci_host *host, u32 val, +						int reg) +{ +	struct bcm2835_sdhci_host *bcm_host = to_bcm(host); + +	/* +	 * The Arasan has a bugette whereby it may lose the content of +	 * successive writes to registers that are within two SD-card clock +	 * cycles of each other (a clock domain crossing problem). +	 * It seems, however, that the data register does not have this problem. +	 * (Which is just as well - otherwise we'd have to nobble the DMA engine +	 * too) +	 */ +	while (get_timer(bcm_host->last_write) < bcm_host->twoticks_delay) +		; + +	writel(val, host->ioaddr + reg); +	bcm_host->last_write = get_timer(0); +} + +static inline u32 bcm2835_sdhci_raw_readl(struct sdhci_host *host, int reg) +{ +	return readl(host->ioaddr + reg); +} + +static void bcm2835_sdhci_writel(struct sdhci_host *host, u32 val, int reg) +{ +	bcm2835_sdhci_raw_writel(host, val, reg); +} + +static void bcm2835_sdhci_writew(struct sdhci_host *host, u16 val, int reg) +{ +	static u32 shadow; +	u32 oldval = (reg == SDHCI_COMMAND) ? shadow : +		bcm2835_sdhci_raw_readl(host, reg & ~3); +	u32 word_num = (reg >> 1) & 1; +	u32 word_shift = word_num * 16; +	u32 mask = 0xffff << word_shift; +	u32 newval = (oldval & ~mask) | (val << word_shift); + +	if (reg == SDHCI_TRANSFER_MODE) +		shadow = newval; +	else +		bcm2835_sdhci_raw_writel(host, newval, reg & ~3); +} + +static void bcm2835_sdhci_writeb(struct sdhci_host *host, u8 val, int reg) +{ +	u32 oldval = bcm2835_sdhci_raw_readl(host, reg & ~3); +	u32 byte_num = reg & 3; +	u32 byte_shift = byte_num * 8; +	u32 mask = 0xff << byte_shift; +	u32 newval = (oldval & ~mask) | (val << byte_shift); + +	bcm2835_sdhci_raw_writel(host, newval, reg & ~3); +} + +static u32 bcm2835_sdhci_readl(struct sdhci_host *host, int reg) +{ +	u32 val = bcm2835_sdhci_raw_readl(host, reg); + +	return val; +} + +static u16 bcm2835_sdhci_readw(struct sdhci_host *host, int reg) +{ +	u32 val = bcm2835_sdhci_raw_readl(host, (reg & ~3)); +	u32 word_num = (reg >> 1) & 1; +	u32 word_shift = word_num * 16; +	u32 word = (val >> word_shift) & 0xffff; + +	return word; +} + +static u8 bcm2835_sdhci_readb(struct sdhci_host *host, int reg) +{ +	u32 val = bcm2835_sdhci_raw_readl(host, (reg & ~3)); +	u32 byte_num = reg & 3; +	u32 byte_shift = byte_num * 8; +	u32 byte = (val >> byte_shift) & 0xff; + +	return byte; +} + +static const struct sdhci_ops bcm2835_ops = { +	.write_l = bcm2835_sdhci_writel, +	.write_w = bcm2835_sdhci_writew, +	.write_b = bcm2835_sdhci_writeb, +	.read_l = bcm2835_sdhci_readl, +	.read_w = bcm2835_sdhci_readw, +	.read_b = bcm2835_sdhci_readb, +}; + +int bcm2835_sdhci_init(u32 regbase, u32 emmc_freq) +{ +	struct bcm2835_sdhci_host *bcm_host; +	struct sdhci_host *host; + +	bcm_host = malloc(sizeof(*bcm_host)); +	if (!bcm_host) { +		printf("sdhci_host malloc fail!\n"); +		return 1; +	} + +	/* +	 * See the comments in bcm2835_sdhci_raw_writel(). +	 * +	 * This should probably be dynamically calculated based on the actual +	 * frequency. However, this is the longest we'll have to wait, and +	 * doesn't seem to slow access down too much, so the added complexity +	 * doesn't seem worth it for now. +	 * +	 * 1/MIN_FREQ is (max) time per tick of eMMC clock. +	 * 2/MIN_FREQ is time for two ticks. +	 * Multiply by 1000000 to get uS per two ticks. +	 * +1 for hack rounding. +	 */ +	bcm_host->twoticks_delay = ((2 * 1000000) / MIN_FREQ) + 1; +	bcm_host->last_write = 0; + +	host = &bcm_host->host; +	host->name = "bcm2835_sdhci"; +	host->ioaddr = (void *)regbase; +	host->quirks = SDHCI_QUIRK_BROKEN_VOLTAGE | SDHCI_QUIRK_BROKEN_R1B | +		SDHCI_QUIRK_WAIT_SEND_CMD; +	host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; +	host->ops = &bcm2835_ops; + +	host->version = sdhci_readw(host, SDHCI_HOST_VERSION); +	add_sdhci(host, emmc_freq, MIN_FREQ); + +	return 0; +} diff --git a/drivers/mmc/bfin_sdh.c b/drivers/mmc/bfin_sdh.c index 8d59d46c6..26311741f 100644 --- a/drivers/mmc/bfin_sdh.c +++ b/drivers/mmc/bfin_sdh.c @@ -19,9 +19,7 @@  #include <asm/mach-common/bits/sdh.h>  #include <asm/mach-common/bits/dma.h> -#if defined(__ADSPBF50x__) || defined(__ADSPBF51x__) -# define bfin_read_SDH_PWR_CTL		bfin_read_RSI_PWR_CONTROL -# define bfin_write_SDH_PWR_CTL		bfin_write_RSI_PWR_CONTROL +#if defined(__ADSPBF50x__) || defined(__ADSPBF51x__) || defined(__ADSPBF60x__)  # define bfin_read_SDH_CLK_CTL		bfin_read_RSI_CLK_CONTROL  # define bfin_write_SDH_CLK_CTL		bfin_write_RSI_CLK_CONTROL  # define bfin_write_SDH_ARGUMENT	bfin_write_RSI_ARGUMENT @@ -38,10 +36,21 @@  # define bfin_write_SDH_STATUS_CLR 	bfin_write_RSI_STATUSCL  # define bfin_read_SDH_CFG		bfin_read_RSI_CONFIG  # define bfin_write_SDH_CFG		bfin_write_RSI_CONFIG +# if defined(__ADSPBF60x__) +# define bfin_read_SDH_BLK_SIZE		bfin_read_RSI_BLKSZ +# define bfin_write_SDH_BLK_SIZE	bfin_write_RSI_BLKSZ +# define bfin_write_DMA_START_ADDR	bfin_write_DMA10_START_ADDR +# define bfin_write_DMA_X_COUNT		bfin_write_DMA10_X_COUNT +# define bfin_write_DMA_X_MODIFY	bfin_write_DMA10_X_MODIFY +# define bfin_write_DMA_CONFIG		bfin_write_DMA10_CONFIG +# else +# define bfin_read_SDH_PWR_CTL		bfin_read_RSI_PWR_CONTROL +# define bfin_write_SDH_PWR_CTL		bfin_write_RSI_PWR_CONTROL  # define bfin_write_DMA_START_ADDR	bfin_write_DMA4_START_ADDR  # define bfin_write_DMA_X_COUNT		bfin_write_DMA4_X_COUNT  # define bfin_write_DMA_X_MODIFY	bfin_write_DMA4_X_MODIFY  # define bfin_write_DMA_CONFIG		bfin_write_DMA4_CONFIG +# endif  # define PORTMUX_PINS \  	{ P_RSI_DATA0, P_RSI_DATA1, P_RSI_DATA2, P_RSI_DATA3, P_RSI_CMD, P_RSI_CLK, 0 }  #elif defined(__ADSPBF54x__) @@ -70,6 +79,9 @@ sdh_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd)  		sdh_cmd |= CMD_RSP;  	if (flags & MMC_RSP_136)  		sdh_cmd |= CMD_L_RSP; +#ifdef RSI_BLKSZ +	sdh_cmd |= CMD_DATA0_BUSY; +#endif  	bfin_write_SDH_ARGUMENT(arg);  	bfin_write_SDH_COMMAND(sdh_cmd); @@ -104,6 +116,12 @@ sdh_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd)  	bfin_write_SDH_STATUS_CLR(CMD_SENT_STAT | CMD_RESP_END_STAT |  				CMD_TIMEOUT_STAT | CMD_CRC_FAIL_STAT); +#ifdef RSI_BLKSZ +	/* wait till card ready */ +	while (!(bfin_read_RSI_ESTAT() & SD_CARD_READY)) +		continue; +	bfin_write_RSI_ESTAT(SD_CARD_READY); +#endif  	return ret;  } @@ -113,16 +131,19 @@ static int sdh_setup_data(struct mmc *mmc, struct mmc_data *data)  {  	u16 data_ctl = 0;  	u16 dma_cfg = 0; -	int ret = 0;  	unsigned long data_size = data->blocksize * data->blocks;  	/* Don't support write yet. */  	if (data->flags & MMC_DATA_WRITE)  		return UNUSABLE_ERR; +#ifndef RSI_BLKSZ  	data_ctl |= ((ffs(data_size) - 1) << 4); +#else +	bfin_write_SDH_BLK_SIZE(data_size); +#endif  	data_ctl |= DTX_DIR;  	bfin_write_SDH_DATA_CTL(data_ctl); -	dma_cfg = WDSIZE_32 | RESTART | WNR | DMAEN; +	dma_cfg = WDSIZE_32 | PSIZE_32 | RESTART | WNR | DMAEN;  	bfin_write_SDH_DATA_TIMER(-1); @@ -137,7 +158,7 @@ static int sdh_setup_data(struct mmc *mmc, struct mmc_data *data)  	/* kick off transfer */  	bfin_write_SDH_DATA_CTL(bfin_read_SDH_DATA_CTL() | DTX_DMA_E | DTX_E); -	return ret; +	return 0;  } @@ -147,13 +168,23 @@ static int bfin_sdh_request(struct mmc *mmc, struct mmc_cmd *cmd,  	u32 status;  	int ret = 0; +	if (data) { +		ret = sdh_setup_data(mmc, data); +		if (ret) +			return ret; +	} +  	ret = sdh_send_cmd(mmc, cmd);  	if (ret) { +		bfin_write_SDH_COMMAND(0); +		bfin_write_DMA_CONFIG(0); +		bfin_write_SDH_DATA_CTL(0); +		SSYNC();  		printf("sending CMD%d failed\n", cmd->cmdidx);  		return ret;  	} +  	if (data) { -		ret = sdh_setup_data(mmc, data);  		do {  			udelay(1);  			status = bfin_read_SDH_STATUS(); @@ -208,10 +239,12 @@ static void bfin_sdh_set_ios(struct mmc *mmc)  	if (mmc->bus_width == 4) {  		cfg = bfin_read_SDH_CFG(); -		cfg &= ~0x80; -		cfg |= 0x40; +#ifndef RSI_BLKSZ +		cfg &= ~PD_SDDAT3; +#endif +		cfg |= PUP_SDDAT3;  		bfin_write_SDH_CFG(cfg); -		clk_ctl |= WIDE_BUS; +		clk_ctl |= WIDE_BUS_4;  	}  	bfin_write_SDH_CLK_CTL(clk_ctl);  	sdh_set_clk(mmc->clock); @@ -220,20 +253,23 @@ static void bfin_sdh_set_ios(struct mmc *mmc)  static int bfin_sdh_init(struct mmc *mmc)  {  	const unsigned short pins[] = PORTMUX_PINS; -	u16 pwr_ctl = 0; +	int ret;  	/* Initialize sdh controller */ -	peripheral_request_list(pins, "bfin_sdh"); +	ret = peripheral_request_list(pins, "bfin_sdh"); +	if (ret < 0) +		return ret;  #if defined(__ADSPBF54x__)  	bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1);  #endif  	bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN);  	/* Disable card detect pin */  	bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | 0x60); - -	pwr_ctl |= ROD_CTL; -	pwr_ctl |= PWR_ON; -	bfin_write_SDH_PWR_CTL(pwr_ctl); +#ifndef RSI_BLKSZ +	bfin_write_SDH_PWR_CTL(PWR_ON | ROD_CTL); +#else +	bfin_write_SDH_CFG(bfin_read_SDH_CFG() | PWR_ON); +#endif  	return 0;  } @@ -251,6 +287,7 @@ int bfin_mmc_init(bd_t *bis)  	mmc->set_ios = bfin_sdh_set_ios;  	mmc->init = bfin_sdh_init;  	mmc->getcd = NULL; +	mmc->getwp = NULL;  	mmc->host_caps = MMC_MODE_4BIT;  	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; diff --git a/drivers/mmc/davinci_mmc.c b/drivers/mmc/davinci_mmc.c index ee8f2614d..e2379e326 100644 --- a/drivers/mmc/davinci_mmc.c +++ b/drivers/mmc/davinci_mmc.c @@ -388,6 +388,7 @@ int davinci_mmc_init(bd_t *bis, struct davinci_mmc *host)  	mmc->set_ios = dmmc_set_ios;  	mmc->init = dmmc_init;  	mmc->getcd = NULL; +	mmc->getwp = NULL;  	mmc->f_min = 200000;  	mmc->f_max = 25000000; diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index b90f3e776..54b536316 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -552,6 +552,7 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)  	mmc->set_ios = esdhc_set_ios;  	mmc->init = esdhc_init;  	mmc->getcd = esdhc_getcd; +	mmc->getwp = NULL;  	voltage_caps = 0;  	caps = regs->hostcapblt; diff --git a/drivers/mmc/ftsdc010_esdhc.c b/drivers/mmc/ftsdc010_esdhc.c index f1702fe33..42f0e0ce5 100644 --- a/drivers/mmc/ftsdc010_esdhc.c +++ b/drivers/mmc/ftsdc010_esdhc.c @@ -666,6 +666,7 @@ int ftsdc010_mmc_init(int dev_index)  	mmc->set_ios = ftsdc010_set_ios;  	mmc->init = ftsdc010_core_init;  	mmc->getcd = NULL; +	mmc->getwp = NULL;  	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c index 67b2dbe8d..70a9f91c8 100644 --- a/drivers/mmc/gen_atmel_mci.c +++ b/drivers/mmc/gen_atmel_mci.c @@ -349,6 +349,7 @@ int atmel_mci_init(void *regs)  	mmc->set_ios = mci_set_ios;  	mmc->init = mci_init;  	mmc->getcd = NULL; +	mmc->getwp = NULL;  	/* need to be able to pass these in on a board by board basis */  	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 72e8ce6da..d732581eb 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -40,6 +40,27 @@  static struct list_head mmc_devices;  static int cur_dev_num = -1; +int __weak board_mmc_getwp(struct mmc *mmc) +{ +	return -1; +} + +int mmc_getwp(struct mmc *mmc) +{ +	int wp; + +	wp = board_mmc_getwp(mmc); + +	if (wp < 0) { +		if (mmc->getwp) +			wp = mmc->getwp(mmc); +		else +			wp = 0; +	} + +	return wp; +} +  int __board_mmc_getcd(struct mmc *mmc) {  	return -1;  } @@ -675,8 +696,12 @@ int mmc_getcd(struct mmc *mmc)  	cd = board_mmc_getcd(mmc); -	if ((cd < 0) && mmc->getcd) -		cd = mmc->getcd(mmc); +	if (cd < 0) { +		if (mmc->getcd) +			cd = mmc->getcd(mmc); +		else +			cd = 1; +	}  	return cd;  } diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c index 11ba532b0..fe6a5a166 100644 --- a/drivers/mmc/mmc_spi.c +++ b/drivers/mmc/mmc_spi.c @@ -273,6 +273,7 @@ struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode)  	mmc->set_ios = mmc_spi_set_ios;  	mmc->init = mmc_spi_init_p;  	mmc->getcd = NULL; +	mmc->getwp = NULL;  	mmc->host_caps = MMC_MODE_SPI;  	mmc->voltages = MMC_SPI_VOLTAGE; diff --git a/drivers/mmc/mxcmmc.c b/drivers/mmc/mxcmmc.c index d58c18bc2..4f99617b9 100644 --- a/drivers/mmc/mxcmmc.c +++ b/drivers/mmc/mxcmmc.c @@ -499,6 +499,7 @@ static int mxcmci_initialize(bd_t *bis)  	mmc->set_ios = mxcmci_set_ios;  	mmc->init = mxcmci_init;  	mmc->getcd = NULL; +	mmc->getwp = NULL;  	mmc->host_caps = MMC_MODE_4BIT;  	host->base = (struct mxcmci_regs *)CONFIG_MXC_MCI_REGS_BASE; diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c index a72f66cc7..a89660f13 100644 --- a/drivers/mmc/mxsmmc.c +++ b/drivers/mmc/mxsmmc.c @@ -53,12 +53,6 @@ struct mxsmmc_priv {  	struct mxs_dma_desc	*desc;  }; -#if defined(CONFIG_MX23) -static const unsigned int mxsmmc_id_offset = 1; -#elif defined(CONFIG_MX28) -static const unsigned int mxsmmc_id_offset = 0; -#endif -  #define	MXSMMC_MAX_TIMEOUT	10000  #define MXSMMC_SMALL_TRANSFER	512 @@ -137,7 +131,7 @@ static int mxsmmc_send_cmd_dma(struct mxsmmc_priv *priv, struct mmc_data *data)  	priv->desc->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM |  				(data_count << MXS_DMA_DESC_BYTES_OFFSET); -	dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + priv->id + mxsmmc_id_offset; +	dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + priv->id;  	mxs_dma_desc_append(dmach, priv->desc);  	if (mxs_dma_go(dmach)) {  		bounce_buffer_stop(&bbstate); @@ -390,15 +384,9 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int), int (*cd)(int))  	struct mmc *mmc = NULL;  	struct mxsmmc_priv *priv = NULL;  	int ret; -#if defined(CONFIG_MX23) -	const unsigned int mxsmmc_max_id = 2; -	const unsigned int mxsmmc_clk_id = 0; -#elif defined(CONFIG_MX28) -	const unsigned int mxsmmc_max_id = 4; -	const unsigned int mxsmmc_clk_id = id; -#endif +	const unsigned int mxsmmc_clk_id = mxs_ssp_clock_by_bus(id); -	if (id >= mxsmmc_max_id) +	if (!mxs_ssp_bus_id_valid(id))  		return -ENODEV;  	mmc = malloc(sizeof(struct mmc)); @@ -418,7 +406,7 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int), int (*cd)(int))  		return -ENOMEM;  	} -	ret = mxs_dma_init_channel(id + mxsmmc_id_offset); +	ret = mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + id);  	if (ret)  		return ret; @@ -432,6 +420,7 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int), int (*cd)(int))  	mmc->set_ios = mxsmmc_set_ios;  	mmc->init = mxsmmc_init;  	mmc->getcd = NULL; +	mmc->getwp = NULL;  	mmc->priv = priv;  	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index afd9b30b5..166744c32 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -30,6 +30,7 @@  #include <twl4030.h>  #include <twl6030.h>  #include <twl6035.h> +#include <asm/gpio.h>  #include <asm/io.h>  #include <asm/arch/mmc_host_def.h>  #include <asm/arch/sys_proto.h> @@ -38,30 +39,71 @@  #define SYSCTL_SRC	(1 << 25)  #define SYSCTL_SRD	(1 << 26) +struct omap_hsmmc_data { +	struct hsmmc *base_addr; +	int cd_gpio; +	int wp_gpio; +}; +  /* If we fail after 1 second wait, something is really bad */  #define MAX_RETRY_MS	1000  static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size);  static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,  			unsigned int siz); -static struct mmc hsmmc_dev[2]; +static struct mmc hsmmc_dev[3]; +static struct omap_hsmmc_data hsmmc_dev_data[3]; + +#if (defined(CONFIG_OMAP_GPIO) && !defined(CONFIG_SPL_BUILD)) || \ +	(defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_GPIO_SUPPORT)) +static int omap_mmc_setup_gpio_in(int gpio, const char *label) +{ +	if (!gpio_is_valid(gpio)) +		return -1; + +	if (gpio_request(gpio, label) < 0) +		return -1; + +	if (gpio_direction_input(gpio) < 0) +		return -1; + +	return gpio; +} + +static int omap_mmc_getcd(struct mmc *mmc) +{ +	int cd_gpio = ((struct omap_hsmmc_data *)mmc->priv)->cd_gpio; +	return gpio_get_value(cd_gpio); +} + +static int omap_mmc_getwp(struct mmc *mmc) +{ +	int wp_gpio = ((struct omap_hsmmc_data *)mmc->priv)->wp_gpio; +	return gpio_get_value(wp_gpio); +} +#else +static inline int omap_mmc_setup_gpio_in(int gpio, const char *label) +{ +	return -1; +} + +#define omap_mmc_getcd NULL +#define omap_mmc_getwp NULL +#endif  #if defined(CONFIG_OMAP44XX) && defined(CONFIG_TWL6030_POWER)  static void omap4_vmmc_pbias_config(struct mmc *mmc)  {  	u32 value = 0; -	struct omap_sys_ctrl_regs *const ctrl = -		(struct omap_sys_ctrl_regs *) SYSCTRL_GENERAL_CORE_BASE; - -	value = readl(&ctrl->control_pbiaslite); +	value = readl((*ctrl)->control_pbiaslite);  	value &= ~(MMC1_PBIASLITE_PWRDNZ | MMC1_PWRDNZ); -	writel(value, &ctrl->control_pbiaslite); +	writel(value, (*ctrl)->control_pbiaslite);  	/* set VMMC to 3V */  	twl6030_power_mmc_init(); -	value = readl(&ctrl->control_pbiaslite); +	value = readl((*ctrl)->control_pbiaslite);  	value |= MMC1_PBIASLITE_VMODE | MMC1_PBIASLITE_PWRDNZ | MMC1_PWRDNZ; -	writel(value, &ctrl->control_pbiaslite); +	writel(value, (*ctrl)->control_pbiaslite);  }  #endif @@ -69,26 +111,24 @@ static void omap4_vmmc_pbias_config(struct mmc *mmc)  static void omap5_pbias_config(struct mmc *mmc)  {  	u32 value = 0; -	struct omap_sys_ctrl_regs *const ctrl = -		(struct omap_sys_ctrl_regs *) SYSCTRL_GENERAL_CORE_BASE; -	value = readl(&ctrl->control_pbias); +	value = readl((*ctrl)->control_pbias);  	value &= ~(SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ);  	value |= SDCARD_BIAS_HIZ_MODE; -	writel(value, &ctrl->control_pbias); +	writel(value, (*ctrl)->control_pbias);  	twl6035_mmc1_poweron_ldo(); -	value = readl(&ctrl->control_pbias); +	value = readl((*ctrl)->control_pbias);  	value &= ~SDCARD_BIAS_HIZ_MODE;  	value |= SDCARD_PBIASLITE_VMODE | SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ; -	writel(value, &ctrl->control_pbias); +	writel(value, (*ctrl)->control_pbias); -	value = readl(&ctrl->control_pbias); +	value = readl((*ctrl)->control_pbias);  	if (value & (1 << 23)) {  		value &= ~(SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ);  		value |= SDCARD_BIAS_HIZ_MODE; -		writel(value, &ctrl->control_pbias); +		writel(value, (*ctrl)->control_pbias);  	}  }  #endif @@ -177,11 +217,12 @@ void mmc_init_stream(struct hsmmc *mmc_base)  static int mmc_init_setup(struct mmc *mmc)  { -	struct hsmmc *mmc_base = (struct hsmmc *)mmc->priv; +	struct hsmmc *mmc_base;  	unsigned int reg_val;  	unsigned int dsor;  	ulong start; +	mmc_base = ((struct omap_hsmmc_data *)mmc->priv)->base_addr;  	mmc_board_init(mmc);  	writel(readl(&mmc_base->sysconfig) | MMC_SOFTRESET, @@ -262,10 +303,11 @@ static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit)  static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,  			struct mmc_data *data)  { -	struct hsmmc *mmc_base = (struct hsmmc *)mmc->priv; +	struct hsmmc *mmc_base;  	unsigned int flags, mmc_stat;  	ulong start; +	mmc_base = ((struct omap_hsmmc_data *)mmc->priv)->base_addr;  	start = get_timer(0);  	while ((readl(&mmc_base->pstate) & (DATI_MASK | CMDI_MASK)) != 0) {  		if (get_timer(0) - start > MAX_RETRY_MS) { @@ -489,10 +531,11 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,  static void mmc_set_ios(struct mmc *mmc)  { -	struct hsmmc *mmc_base = (struct hsmmc *)mmc->priv; +	struct hsmmc *mmc_base;  	unsigned int dsor = 0;  	ulong start; +	mmc_base = ((struct omap_hsmmc_data *)mmc->priv)->base_addr;  	/* configue bus width */  	switch (mmc->bus_width) {  	case 8: @@ -540,36 +583,44 @@ static void mmc_set_ios(struct mmc *mmc)  	writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);  } -int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max) +int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, +		int wp_gpio)  { -	struct mmc *mmc; - -	mmc = &hsmmc_dev[dev_index]; +	struct mmc *mmc = &hsmmc_dev[dev_index]; +	struct omap_hsmmc_data *priv_data = &hsmmc_dev_data[dev_index];  	sprintf(mmc->name, "OMAP SD/MMC");  	mmc->send_cmd = mmc_send_cmd;  	mmc->set_ios = mmc_set_ios;  	mmc->init = mmc_init_setup; -	mmc->getcd = NULL; +	mmc->priv = priv_data;  	switch (dev_index) {  	case 0: -		mmc->priv = (struct hsmmc *)OMAP_HSMMC1_BASE; +		priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC1_BASE;  		break;  #ifdef OMAP_HSMMC2_BASE  	case 1: -		mmc->priv = (struct hsmmc *)OMAP_HSMMC2_BASE; +		priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC2_BASE;  		break;  #endif  #ifdef OMAP_HSMMC3_BASE  	case 2: -		mmc->priv = (struct hsmmc *)OMAP_HSMMC3_BASE; +		priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC3_BASE;  		break;  #endif  	default: -		mmc->priv = (struct hsmmc *)OMAP_HSMMC1_BASE; +		priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC1_BASE;  		return 1;  	} +	priv_data->cd_gpio = omap_mmc_setup_gpio_in(cd_gpio, "mmc_cd"); +	if (priv_data->cd_gpio != -1) +		mmc->getcd = omap_mmc_getcd; + +	priv_data->wp_gpio = omap_mmc_setup_gpio_in(wp_gpio, "mmc_wp"); +	if (priv_data->wp_gpio != -1) +		mmc->getwp = omap_mmc_getwp; +  	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;  	mmc->host_caps = (MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS |  				MMC_MODE_HC) & ~host_caps_mask; diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 551b4232c..1eaea04ad 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -440,6 +440,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)  	mmc->set_ios = sdhci_set_ios;  	mmc->init = sdhci_init;  	mmc->getcd = NULL; +	mmc->getwp = NULL;  	caps = sdhci_readl(host, SDHCI_CAPABILITIES);  #ifdef CONFIG_MMC_SDMA diff --git a/drivers/mmc/sh_mmcif.c b/drivers/mmc/sh_mmcif.c index 4588568a6..011d4f3e6 100644 --- a/drivers/mmc/sh_mmcif.c +++ b/drivers/mmc/sh_mmcif.c @@ -599,6 +599,7 @@ int mmcif_mmc_init(void)  	mmc->set_ios = sh_mmcif_set_ios;  	mmc->init = sh_mmcif_init;  	mmc->getcd = NULL; +	mmc->getwp = NULL;  	host->regs = (struct sh_mmcif_regs *)CONFIG_SH_MMCIF_ADDR;  	host->clk = CONFIG_SH_MMCIF_CLK;  	mmc->priv = host; diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index d749ab095..e86bc680f 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -2,7 +2,7 @@   * (C) Copyright 2009 SAMSUNG Electronics   * Minkyu Kang <mk7.kang@samsung.com>   * Jaehoon Chung <jh80.chung@samsung.com> - * Portions Copyright 2011-2012 NVIDIA Corporation + * Portions Copyright 2011-2013 NVIDIA Corporation   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License as published by @@ -28,43 +28,45 @@  #include <asm/arch-tegra/tegra_mmc.h>  #include <mmc.h> -/* support 4 mmc hosts */ -struct mmc mmc_dev[4]; -struct mmc_host mmc_host[4]; +DECLARE_GLOBAL_DATA_PTR; +struct mmc mmc_dev[MAX_HOSTS]; +struct mmc_host mmc_host[MAX_HOSTS]; -/** - * Get the host address and peripheral ID for a device. Devices are numbered - * from 0 to 3. - * - * @param host		Structure to fill in (base, reg, mmc_id) - * @param dev_index	Device index (0-3) - */ -static void tegra_get_setup(struct mmc_host *host, int dev_index) +#ifndef CONFIG_OF_CONTROL +#error "Please enable device tree support to use this driver" +#endif + +static void mmc_set_power(struct mmc_host *host, unsigned short power)  { -	debug("tegra_get_setup: dev_index = %d\n", dev_index); +	u8 pwr = 0; +	debug("%s: power = %x\n", __func__, power); -	switch (dev_index) { -	case 1: -		host->base = TEGRA_SDMMC3_BASE; -		host->mmc_id = PERIPH_ID_SDMMC3; -		break; -	case 2: -		host->base = TEGRA_SDMMC2_BASE; -		host->mmc_id = PERIPH_ID_SDMMC2; -		break; -	case 3: -		host->base = TEGRA_SDMMC1_BASE; -		host->mmc_id = PERIPH_ID_SDMMC1; -		break; -	case 0: -	default: -		host->base = TEGRA_SDMMC4_BASE; -		host->mmc_id = PERIPH_ID_SDMMC4; -		break; +	if (power != (unsigned short)-1) { +		switch (1 << power) { +		case MMC_VDD_165_195: +			pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8; +			break; +		case MMC_VDD_29_30: +		case MMC_VDD_30_31: +			pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0; +			break; +		case MMC_VDD_32_33: +		case MMC_VDD_33_34: +			pwr = TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3; +			break; +		}  	} +	debug("%s: pwr = %X\n", __func__, pwr); + +	/* Set the bus voltage first (if any) */ +	writeb(pwr, &host->reg->pwrcon); +	if (pwr == 0) +		return; -	host->reg = (struct tegra_mmc *)host->base; +	/* Now enable bus power */ +	pwr |= TEGRA_MMC_PWRCTL_SD_BUS_POWER; +	writeb(pwr, &host->reg->pwrcon);  }  static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data, @@ -363,8 +365,7 @@ static void mmc_change_clock(struct mmc_host *host, uint clock)  	debug(" mmc_change_clock called\n");  	/* -	 * Change Tegra SDMMCx clock divisor here. Source is 216MHz, -	 * PLLP_OUT0 +	 * Change Tegra SDMMCx clock divisor here. Source is PLLP_OUT0  	 */  	if (clock == 0)  		goto out; @@ -439,7 +440,7 @@ static void mmc_set_ios(struct mmc *mmc)  	debug("mmc_set_ios: hostctl = %08X\n", ctrl);  } -static void mmc_reset(struct mmc_host *host) +static void mmc_reset(struct mmc_host *host, struct mmc *mmc)  {  	unsigned int timeout;  	debug(" mmc_reset called\n"); @@ -465,6 +466,14 @@ static void mmc_reset(struct mmc_host *host)  		timeout--;  		udelay(1000);  	} + +	/* Set SD bus voltage & enable bus power */ +	mmc_set_power(host, fls(mmc->voltages) - 1); +	debug("%s: power control = %02X, host control = %02X\n", __func__, +		readb(&host->reg->pwrcon), readb(&host->reg->hostctl)); + +	/* Make sure SDIO pads are set up */ +	pad_init_mmc(host);  }  static int mmc_core_init(struct mmc *mmc) @@ -473,7 +482,7 @@ static int mmc_core_init(struct mmc *mmc)  	unsigned int mask;  	debug(" mmc_core_init called\n"); -	mmc_reset(host); +	mmc_reset(host, mmc);  	host->version = readw(&host->reg->hcver);  	debug("host version = %x\n", host->version); @@ -518,41 +527,43 @@ int tegra_mmc_getcd(struct mmc *mmc)  	debug("tegra_mmc_getcd called\n"); -	if (host->cd_gpio >= 0) -		return !gpio_get_value(host->cd_gpio); +	if (fdt_gpio_isvalid(&host->cd_gpio)) +		return fdtdec_get_gpio(&host->cd_gpio);  	return 1;  } -int tegra_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio) +static int do_mmc_init(int dev_index)  {  	struct mmc_host *host;  	char gpusage[12]; /* "SD/MMCn PWR" or "SD/MMCn CD" */  	struct mmc *mmc; -	debug(" tegra_mmc_init: index %d, bus width %d " -		"pwr_gpio %d cd_gpio %d\n", -		dev_index, bus_width, pwr_gpio, cd_gpio); - +	/* DT should have been read & host config filled in */  	host = &mmc_host[dev_index]; +	if (!host->enabled) +		return -1; -	host->clock = 0; -	host->pwr_gpio = pwr_gpio; -	host->cd_gpio = cd_gpio; -	tegra_get_setup(host, dev_index); +	debug(" do_mmc_init: index %d, bus width %d " +		"pwr_gpio %d cd_gpio %d\n", +		dev_index, host->width, +		host->pwr_gpio.gpio, host->cd_gpio.gpio); +	host->clock = 0;  	clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); -	if (host->pwr_gpio >= 0) { +	if (fdt_gpio_isvalid(&host->pwr_gpio)) {  		sprintf(gpusage, "SD/MMC%d PWR", dev_index); -		gpio_request(host->pwr_gpio, gpusage); -		gpio_direction_output(host->pwr_gpio, 1); +		gpio_request(host->pwr_gpio.gpio, gpusage); +		gpio_direction_output(host->pwr_gpio.gpio, 1); +		debug(" Power GPIO name = %s\n", host->pwr_gpio.name);  	} -	if (host->cd_gpio >= 0) { +	if (fdt_gpio_isvalid(&host->cd_gpio)) {  		sprintf(gpusage, "SD/MMC%d CD", dev_index); -		gpio_request(host->cd_gpio, gpusage); -		gpio_direction_input(host->cd_gpio); +		gpio_request(host->cd_gpio.gpio, gpusage); +		gpio_direction_input(host->cd_gpio.gpio); +		debug(" CD GPIO name = %s\n", host->cd_gpio.name);  	}  	mmc = &mmc_dev[dev_index]; @@ -563,12 +574,13 @@ int tegra_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)  	mmc->set_ios = mmc_set_ios;  	mmc->init = mmc_core_init;  	mmc->getcd = tegra_mmc_getcd; +	mmc->getwp = NULL;  	mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;  	mmc->host_caps = 0; -	if (bus_width == 8) +	if (host->width == 8)  		mmc->host_caps |= MMC_MODE_8BIT; -	if (bus_width >= 4) +	if (host->width >= 4)  		mmc->host_caps |= MMC_MODE_4BIT;  	mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC; @@ -577,8 +589,6 @@ int tegra_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)  	 *  low-speed SDIO card frequency (actually 400KHz)  	 * max freq is highest HS eMMC clock as per the SD/MMC spec  	 *  (actually 52MHz) -	 * Both of these are the closest equivalents w/216MHz source -	 *  clock and Tegra SDMMC divisors.  	 */  	mmc->f_min = 375000;  	mmc->f_max = 48000000; @@ -587,3 +597,104 @@ int tegra_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio)  	return 0;  } + +/** + * Get the host address and peripheral ID for a node. + * + * @param blob		fdt blob + * @param node		Device index (0-3) + * @param host		Structure to fill in (reg, width, mmc_id) + */ +static int mmc_get_config(const void *blob, int node, struct mmc_host *host) +{ +	debug("%s: node = %d\n", __func__, node); + +	host->enabled = fdtdec_get_is_enabled(blob, node); + +	host->reg = (struct tegra_mmc *)fdtdec_get_addr(blob, node, "reg"); +	if ((fdt_addr_t)host->reg == FDT_ADDR_T_NONE) { +		debug("%s: no sdmmc base reg info found\n", __func__); +		return -FDT_ERR_NOTFOUND; +	} + +	host->mmc_id = clock_decode_periph_id(blob, node); +	if (host->mmc_id == PERIPH_ID_NONE) { +		debug("%s: could not decode periph id\n", __func__); +		return -FDT_ERR_NOTFOUND; +	} + +	/* +	 * NOTE: mmc->bus_width is determined by mmc.c dynamically. +	 * TBD: Override it with this value? +	 */ +	host->width = fdtdec_get_int(blob, node, "bus-width", 0); +	if (!host->width) +		debug("%s: no sdmmc width found\n", __func__); + +	/* These GPIOs are optional */ +	fdtdec_decode_gpio(blob, node, "cd-gpios", &host->cd_gpio); +	fdtdec_decode_gpio(blob, node, "wp-gpios", &host->wp_gpio); +	fdtdec_decode_gpio(blob, node, "power-gpios", &host->pwr_gpio); + +	debug("%s: found controller at %p, width = %d, periph_id = %d\n", +		__func__, host->reg, host->width, host->mmc_id); +	return 0; +} + +/* + * Process a list of nodes, adding them to our list of SDMMC ports. + * + * @param blob          fdt blob + * @param node_list     list of nodes to process (any <=0 are ignored) + * @param count         number of nodes to process + * @return 0 if ok, -1 on error + */ +static int process_nodes(const void *blob, int node_list[], int count) +{ +	struct mmc_host *host; +	int i, node; + +	debug("%s: count = %d\n", __func__, count); + +	/* build mmc_host[] for each controller */ +	for (i = 0; i < count; i++) { +		node = node_list[i]; +		if (node <= 0) +			continue; + +		host = &mmc_host[i]; +		host->id = i; + +		if (mmc_get_config(blob, node, host)) { +			printf("%s: failed to decode dev %d\n",	__func__, i); +			return -1; +		} +		do_mmc_init(i); +	} +	return 0; +} + +void tegra_mmc_init(void) +{ +	int node_list[MAX_HOSTS], count; +	const void *blob = gd->fdt_blob; +	debug("%s entry\n", __func__); + +	/* See if any Tegra30 MMC controllers are present */ +	count = fdtdec_find_aliases_for_id(blob, "sdhci", +		COMPAT_NVIDIA_TEGRA30_SDMMC, node_list, MAX_HOSTS); +	debug("%s: count of T30 sdhci nodes is %d\n", __func__, count); +	if (process_nodes(blob, node_list, count)) { +		printf("%s: Error processing T30 mmc node(s)!\n", __func__); +		return; +	} + +	/* Now look for any Tegra20 MMC controllers */ +	count = fdtdec_find_aliases_for_id(blob, "sdhci", +		COMPAT_NVIDIA_TEGRA20_SDMMC, node_list, MAX_HOSTS); +	debug("%s: count of T20 sdhci nodes is %d\n", __func__, count); +	if (process_nodes(blob, node_list, count)) { +		printf("%s: Error processing T20 mmc node(s)!\n", __func__); +		return; +	} +} diff --git a/drivers/mtd/nand/kmeter1_nand.c b/drivers/mtd/nand/kmeter1_nand.c index e8e5b7b85..f04459723 100644 --- a/drivers/mtd/nand/kmeter1_nand.c +++ b/drivers/mtd/nand/kmeter1_nand.c @@ -119,7 +119,11 @@ static int kpn_nand_dev_ready(struct mtd_info *mtd)  int board_nand_init(struct nand_chip *nand)  { +#if defined(CONFIG_NAND_ECC_BCH) +	nand->ecc.mode = NAND_ECC_SOFT_BCH; +#else  	nand->ecc.mode = NAND_ECC_SOFT; +#endif  	/* Reference hardware control function */  	nand->cmd_ctrl  = kpn_nand_hwcontrol; diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 2ba0c5ef9..ff2d34830 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -237,6 +237,14 @@ int nand_lock(struct mtd_info *mtd, int tight)  	/* select the NAND device */  	chip->select_chip(mtd, 0); +	/* check the Lock Tight Status */ +	chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, 0); +	if (chip->read_byte(mtd) & NAND_LOCK_STATUS_TIGHT) { +		printf("nand_lock: Device is locked tight!\n"); +		ret = -1; +		goto out; +	} +  	chip->cmdfunc(mtd,  		      (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK),  		      -1, -1); @@ -249,6 +257,7 @@ int nand_lock(struct mtd_info *mtd, int tight)  		ret = -1;  	} + out:  	/* de-select the NAND device */  	chip->select_chip(mtd, -1);  	return ret; @@ -337,6 +346,15 @@ int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length,  		goto out;  	} +	/* check the Lock Tight Status */ +	page = (int)(start >> chip->page_shift); +	chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask); +	if (chip->read_byte(mtd) & NAND_LOCK_STATUS_TIGHT) { +		printf("nand_unlock: Device is locked tight!\n"); +		ret = -1; +		goto out; +	} +  	if ((start & (mtd->erasesize - 1)) != 0) {  		printf("nand_unlock: Start address must be beginning of "  			"nand block!\n"); @@ -358,7 +376,6 @@ int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length,  	length -= mtd->erasesize;  	/* submit address of first page to unlock */ -	page = (int)(start >> chip->page_shift);  	chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask);  	/* submit ADDRESS of LAST page to unlock */ diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index cee394ece..bbf5443ec 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -25,6 +25,7 @@  #include <asm/io.h>  #include <asm/errno.h>  #include <asm/arch/mem.h> +#include <asm/arch/cpu.h>  #include <asm/arch/omap_gpmc.h>  #include <linux/mtd/nand_ecc.h>  #include <linux/compiler.h> diff --git a/drivers/mtd/onenand/onenand_spl.c b/drivers/mtd/onenand/onenand_spl.c index 50eaa7188..4bec2c2ad 100644 --- a/drivers/mtd/onenand/onenand_spl.c +++ b/drivers/mtd/onenand/onenand_spl.c @@ -112,7 +112,7 @@ static int onenand_spl_read_page(uint32_t block, uint32_t page, uint32_t *buf,  void onenand_spl_load_image(uint32_t offs, uint32_t size, void *dst)  {  	uint32_t *addr = (uint32_t *)dst; -	uint32_t total_pages; +	uint32_t to_page;  	uint32_t block;  	uint32_t page, rpage;  	enum onenand_spl_pagesize pagesize; @@ -125,22 +125,20 @@ void onenand_spl_load_image(uint32_t offs, uint32_t size, void *dst)  	 * pulling further unwanted functions into the SPL.  	 */  	if (pagesize == 2048) { -		total_pages = DIV_ROUND_UP(size, 2048);  		page = offs / 2048; +		to_page = page + DIV_ROUND_UP(size, 2048);  	} else { -		total_pages = DIV_ROUND_UP(size, 4096);  		page = offs / 4096; +		to_page = page + DIV_ROUND_UP(size, 4096);  	} -	for (; page <= total_pages; page++) { +	for (; page <= to_page; page++) {  		block = page / ONENAND_PAGES_PER_BLOCK;  		rpage = page & (ONENAND_PAGES_PER_BLOCK - 1);  		ret = onenand_spl_read_page(block, rpage, addr, pagesize); -		if (ret) { -			total_pages += ONENAND_PAGES_PER_BLOCK; +		if (ret)  			page += ONENAND_PAGES_PER_BLOCK - 1; -		} else { +		else  			addr += pagesize / 4; -		}  	}  } diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index db04795df..7a3685019 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -24,6 +24,7 @@  #include <asm/errno.h>  #include <asm/io.h>  #include <phy.h> +#include <asm/arch/cpu.h>  #define BITMASK(bits)		(BIT(bits) - 1)  #define PHY_REG_MASK		0x1f @@ -227,6 +228,9 @@ struct cpsw_priv {  	struct cpsw_slave		*slaves;  	struct phy_device		*phydev;  	struct mii_dev			*bus; + +	u32				mdio_link; +	u32				phy_mask;  };  static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32 bits) @@ -598,10 +602,21 @@ static int cpsw_update_link(struct cpsw_priv *priv)  	for_each_slave(slave, priv)  		cpsw_slave_update_link(slave, priv, &link); - +	priv->mdio_link = readl(&mdio_regs->link);  	return link;  } +static int cpsw_check_link(struct cpsw_priv *priv) +{ +	u32 link = 0; + +	link = __raw_readl(&mdio_regs->link) & priv->phy_mask; +	if ((link) && (link == priv->mdio_link)) +		return 1; + +	return cpsw_update_link(priv); +} +  static inline u32  cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num)  {  	if (priv->host_port == 0) @@ -631,6 +646,8 @@ static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv)  	cpsw_ale_port_state(priv, slave_port, ALE_PORT_STATE_FORWARD);  	cpsw_ale_add_mcast(priv, NetBcastAddr, 1 << slave_port); + +	priv->phy_mask |= 1 << slave->data->phy_id;  }  static struct cpdma_desc *cpdma_desc_alloc(struct cpsw_priv *priv) @@ -862,7 +879,7 @@ static int cpsw_send(struct eth_device *dev, void *packet, int length)  	int len;  	int timeout = CPDMA_TIMEOUT; -	if (!cpsw_update_link(priv)) +	if (!cpsw_check_link(priv))  		return -EIO;  	flush_dcache_range((unsigned long)packet, diff --git a/drivers/net/fm/fm.c b/drivers/net/fm/fm.c index 49c74c278..8d7058693 100644 --- a/drivers/net/fm/fm.c +++ b/drivers/net/fm/fm.c @@ -362,7 +362,6 @@ static void fm_init_qmi(struct fm_qmi_common *qmi)  int fm_init_common(int index, struct ccsr_fman *reg)  {  	int rc; -	char env_addr[32];  #if defined(CONFIG_SYS_QE_FMAN_FW_IN_NOR)  	void *addr = (void *)CONFIG_SYS_QE_FMAN_FW_ADDR;  #elif defined(CONFIG_SYS_QE_FMAN_FW_IN_NAND) @@ -416,8 +415,7 @@ int fm_init_common(int index, struct ccsr_fman *reg)  	rc = fman_upload_firmware(index, ®->fm_imem, addr);  	if (rc)  		return rc; -	sprintf(env_addr, "0x%lx", (long unsigned int)addr); -	setenv("fman_ucode", env_addr); +	setenv_addr("fman_ucode", addr);  	fm_init_muram(index, ®->muram);  	fm_init_qmi(®->fm_qmi_common); diff --git a/drivers/rtc/mk48t59.c b/drivers/rtc/mk48t59.c index e2858232d..3af2623eb 100644 --- a/drivers/rtc/mk48t59.c +++ b/drivers/rtc/mk48t59.c @@ -49,20 +49,6 @@ static void rtc_write (short reg, uchar val)  	out8(RTC_PORT_DATA, val);  } -#elif defined(CONFIG_PCIPPC2) - -#include "../board/pcippc2/pcippc2.h" - -static uchar rtc_read (short reg) -{ -	return in8(RTC(reg)); -} - -static void rtc_write (short reg, uchar val) -{ -	out8(RTC(reg),val); -} -  #elif defined(CONFIG_EVAL5200)  static uchar rtc_read (short reg) diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 5e8b64873..de3f47199 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -33,7 +33,6 @@ COBJS-$(CONFIG_ARM_DCC) += arm_dcc.o  COBJS-$(CONFIG_ATMEL_USART) += atmel_usart.o  COBJS-$(CONFIG_LPC32XX_HSUART) += lpc32xx_hsuart.o  COBJS-$(CONFIG_MCFUART) += mcfuart.o -COBJS-$(CONFIG_NS9750_UART) += ns9750_serial.o  COBJS-$(CONFIG_OPENCORES_YANU) += opencores_yanu.o  COBJS-$(CONFIG_SYS_NS16550) += ns16550.o  COBJS-$(CONFIG_S3C64XX) += s3c64xx.o diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 87a091708..ed4e6b3a1 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -43,10 +43,10 @@ void NS16550_init(NS16550_t com_port, int baud_divisor)  	serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier);  #if (defined(CONFIG_OMAP) && !defined(CONFIG_OMAP3_ZOOM2)) || \ -					defined(CONFIG_AM33XX) +			defined(CONFIG_AM33XX) || defined(CONFIG_TI814X)  	serial_out(0x7, &com_port->mdr1);	/* mode select reset TL16C750*/  #endif -	serial_out(UART_LCR_BKSE | UART_LCRVAL, (ulong)&com_port->lcr); +	serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr);  	serial_out(0, &com_port->dll);  	serial_out(0, &com_port->dlm);  	serial_out(UART_LCRVAL, &com_port->lcr); @@ -57,7 +57,8 @@ void NS16550_init(NS16550_t com_port, int baud_divisor)  	serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm);  	serial_out(UART_LCRVAL, &com_port->lcr);  #if (defined(CONFIG_OMAP) && !defined(CONFIG_OMAP3_ZOOM2)) || \ -	defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) +	defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \ +	defined(CONFIG_TI814X)  #if defined(CONFIG_APTIX)  	/* /13 mode so Aptix 6MHz can hit 115200 */ diff --git a/drivers/serial/ns9750_serial.c b/drivers/serial/ns9750_serial.c deleted file mode 100644 index 85fc68a07..000000000 --- a/drivers/serial/ns9750_serial.c +++ /dev/null @@ -1,218 +0,0 @@ -/*********************************************************************** - * - * Copyright (C) 2004 by FS Forth-Systeme GmbH. - * All rights reserved. - * - * $Id: ns9750_serial.c,v 1.1 2004/02/16 10:37:20 mpietrek Exp $ - * @Author: Markus Pietrek - * @Descr: Serial driver for the NS9750. Only one UART is supported yet. - * @References: [1] NS9750 Hardware Reference/December 2003 - * @TODO: Implement Character GAP Timer when chip is fixed for PLL bypass - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - * - ***********************************************************************/ - -#include <common.h> - -#include "ns9750_bbus.h"	/* for GPIOs */ -#include "ns9750_ser.h"		/* for serial configuration */ - -DECLARE_GLOBAL_DATA_PTR; - -#if !defined(CONFIG_CONS_INDEX) -#error "No console index specified." -#endif - -#define CONSOLE CONFIG_CONS_INDEX - -static unsigned int calcBitrateRegister( void ); -static unsigned int calcRxCharGapRegister( void ); - -static char cCharsAvailable; /* Numbers of chars in unCharCache */ -static unsigned int unCharCache; /* unCharCache is only valid if -				  * cCharsAvailable > 0 */ - -/*********************************************************************** - * @Function: serial_init - * @Return: 0 - * @Descr: configures GPIOs and UART. Requires BBUS Master Reset turned off - ***********************************************************************/ - -static int ns9750_serial_init(void) -{ -	unsigned int aunGPIOTxD[] = { 0, 8, 40, 44 }; -	unsigned int aunGPIORxD[] = { 1, 9, 41, 45 }; - -	cCharsAvailable = 0; - -	/* configure TxD and RxD pins for their special function */ -	set_gpio_cfg_reg_val( aunGPIOTxD[ CONSOLE ], -			      NS9750_GPIO_CFG_FUNC_0 | NS9750_GPIO_CFG_OUTPUT ); -	set_gpio_cfg_reg_val( aunGPIORxD[ CONSOLE ], -			      NS9750_GPIO_CFG_FUNC_0 | NS9750_GPIO_CFG_INPUT ); - -	/* configure serial engine */ -	*get_ser_reg_addr_channel( NS9750_SER_CTRL_A, CONSOLE ) = -		NS9750_SER_CTRL_A_CE | -		NS9750_SER_CTRL_A_STOP | -		NS9750_SER_CTRL_A_WLS_8; - -	serial_setbrg(); - -	*get_ser_reg_addr_channel( NS9750_SER_CTRL_B, CONSOLE ) = -		NS9750_SER_CTRL_B_RCGT; - -	return 0; -} - -/*********************************************************************** - * @Function: serial_putc - * @Return: n/a - * @Descr: writes one character to the FIFO. Blocks until FIFO is not full - ***********************************************************************/ - -static void ns9750_serial_putc(const char c) -{ -	if (c == '\n') -		serial_putc( '\r' ); - -	while (!(*get_ser_reg_addr_channel( NS9750_SER_STAT_A, CONSOLE) & -		 NS9750_SER_STAT_A_TRDY ) ) { -		/* do nothing, wait for characters in FIFO sent */ -	} - -	*(volatile char*) get_ser_reg_addr_channel( NS9750_SER_FIFO, -						    CONSOLE) = c; -} - -/*********************************************************************** - * @Function: serial_getc - * @Return: the character read - * @Descr: performs only 8bit accesses to the FIFO. No error handling - ***********************************************************************/ - -static int ns9750_serial_getc(void) -{ -	int i; - -	while (!serial_tstc() ) { -		/* do nothing, wait for incoming characters */ -	} - -	/*  at least one character in unCharCache */ -	i = (int) (unCharCache & 0xff); - -	unCharCache >>= 8; -	cCharsAvailable--; - -	return i; -} - -/*********************************************************************** - * @Function: serial_tstc - * @Return: 0 if no input available, otherwise != 0 - * @Descr: checks for incoming FIFO not empty. Stores the incoming chars in - *	   unCharCache and the numbers of characters in cCharsAvailable - ***********************************************************************/ - -static int ns9750_serial_tstc(void) -{ -	unsigned int unRegCache; - -	if ( cCharsAvailable ) -		return 1; - -	unRegCache = *get_ser_reg_addr_channel( NS9750_SER_STAT_A,CONSOLE ); -	if( unRegCache & NS9750_SER_STAT_A_RBC ) { -		*get_ser_reg_addr_channel( NS9750_SER_STAT_A, CONSOLE ) = -			NS9750_SER_STAT_A_RBC; -		unRegCache = *get_ser_reg_addr_channel( NS9750_SER_STAT_A, -							CONSOLE ); -	} - -	if ( unRegCache & NS9750_SER_STAT_A_RRDY ) { -		cCharsAvailable = (unRegCache & NS9750_SER_STAT_A_RXFDB_MA)>>20; -		if ( !cCharsAvailable ) -			cCharsAvailable = 4; - -		unCharCache = *get_ser_reg_addr_channel( NS9750_SER_FIFO, -							 CONSOLE ); -		return 1; -	} - -	/* no chars available */ -	return 0; -} - -static void ns9750_serial_setbrg(void) -{ -	*get_ser_reg_addr_channel( NS9750_SER_BITRATE, CONSOLE ) = -		calcBitrateRegister(); -	*get_ser_reg_addr_channel( NS9750_SER_RX_CHAR_TIMER, CONSOLE ) = -		calcRxCharGapRegister(); -} - -/*********************************************************************** - * @Function: calcBitrateRegister - * @Return: value for the serial bitrate register - * @Descr: register value depends on clock frequency and baudrate - ***********************************************************************/ - -static unsigned int calcBitrateRegister( void ) -{ -	return ( NS9750_SER_BITRATE_EBIT | -		 NS9750_SER_BITRATE_CLKMUX_BCLK | -		 NS9750_SER_BITRATE_TMODE | -		 NS9750_SER_BITRATE_TCDR_16 | -		 NS9750_SER_BITRATE_RCDR_16 | -		 ( ( ( ( CONFIG_SYS_CLK_FREQ / 8 ) / /* BBUS clock,[1] Fig. 38 */ -		       ( gd->baudrate * 16 ) ) - 1 ) & -		   NS9750_SER_BITRATE_N_MA ) ); -} - -/*********************************************************************** - * @Function: calcRxCharGapRegister - * @Return: value for the character gap timer register - * @Descr: register value depends on clock frequency and baudrate. Currently 0 - *	   is used as there is a bug with the gap timer in PLL bypass mode. - ***********************************************************************/ - -static unsigned int calcRxCharGapRegister( void ) -{ -	return NS9750_SER_RX_CHAR_TIMER_TRUN; -} - -static struct serial_device ns9750_serial_drv = { -	.name	= "ns9750_serial", -	.start	= ns9750_serial_init, -	.stop	= NULL, -	.setbrg	= ns9750_serial_setbrg, -	.putc	= ns9750_serial_putc, -	.puts	= default_serial_puts, -	.getc	= ns9750_serial_getc, -	.tstc	= ns9750_serial_tstc, -}; - -void ns9750_serial_initialize(void) -{ -	serial_register(&ns9750_serial_drv); -} - -__weak struct serial_device *default_serial_console(void) -{ -	return &ns9750_serial_drv; -} diff --git a/drivers/serial/sandbox.c b/drivers/serial/sandbox.c index cb19401df..b73520ca9 100644 --- a/drivers/serial/sandbox.c +++ b/drivers/serial/sandbox.c @@ -30,6 +30,19 @@  #include <serial.h>  #include <linux/compiler.h> +/* + * + *   serial_buf: A buffer that holds keyboard characters for the + *		 Sandbox U-boot. + * + * invariants: + *   serial_buf_write		 == serial_buf_read -> empty buffer + *   (serial_buf_write + 1) % 16 == serial_buf_read -> full buffer + */ +static char serial_buf[16]; +static unsigned int serial_buf_write; +static unsigned int serial_buf_read; +  static int sandbox_serial_init(void)  {  	os_tty_raw(0); @@ -50,18 +63,37 @@ static void sandbox_serial_puts(const char *str)  	os_write(1, str, strlen(str));  } -static int sandbox_serial_getc(void) +static unsigned int increment_buffer_index(unsigned int index) +{ +	return (index + 1) % ARRAY_SIZE(serial_buf); +} + +static int sandbox_serial_tstc(void)  { -	char buf; +	const unsigned int next_index = +		increment_buffer_index(serial_buf_write);  	ssize_t count; -	count = os_read(0, &buf, 1); -	return count == 1 ? buf : 0; +	os_usleep(100); +	if (next_index == serial_buf_read) +		return 1;	/* buffer full */ + +	count = os_read_no_block(0, &serial_buf[serial_buf_write], 1); +	if (count == 1) +		serial_buf_write = next_index; +	return serial_buf_write != serial_buf_read;  } -static int sandbox_serial_tstc(void) +static int sandbox_serial_getc(void)  { -	return 0; +	int result; + +	while (!sandbox_serial_tstc()) +		;	/* buffer empty */ + +	result = serial_buf[serial_buf_read]; +	serial_buf_read = increment_buffer_index(serial_buf_read); +	return result;  }  static struct serial_device sandbox_serial_drv = { diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index 1f8955a0f..7922bf066 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -164,7 +164,6 @@ serial_initfunc(altera_serial_initialize);  serial_initfunc(atmel_serial_initialize);  serial_initfunc(lpc32xx_serial_initialize);  serial_initfunc(mcf_serial_initialize); -serial_initfunc(ns9750_serial_initialize);  serial_initfunc(oc_serial_initialize);  serial_initfunc(s3c64xx_serial_initialize);  serial_initfunc(sandbox_serial_initialize); @@ -259,7 +258,6 @@ void serial_initialize(void)  	atmel_serial_initialize();  	lpc32xx_serial_initialize();  	mcf_serial_initialize(); -	ns9750_serial_initialize();  	oc_serial_initialize();  	s3c64xx_serial_initialize();  	sandbox_serial_initialize(); diff --git a/drivers/serial/serial_ns16550.c b/drivers/serial/serial_ns16550.c index fc01a3c51..b92eef4db 100644 --- a/drivers/serial/serial_ns16550.c +++ b/drivers/serial/serial_ns16550.c @@ -247,24 +247,36 @@ serial_setbrg_dev(unsigned int dev_index)  	_serial_setbrg(dev_index);  } +#if defined(CONFIG_SYS_NS16550_COM1)  DECLARE_ESERIAL_FUNCTIONS(1);  struct serial_device eserial1_device =  	INIT_ESERIAL_STRUCTURE(1, "eserial0"); +#endif +#if defined(CONFIG_SYS_NS16550_COM2)  DECLARE_ESERIAL_FUNCTIONS(2);  struct serial_device eserial2_device =  	INIT_ESERIAL_STRUCTURE(2, "eserial1"); +#endif +#if defined(CONFIG_SYS_NS16550_COM3)  DECLARE_ESERIAL_FUNCTIONS(3);  struct serial_device eserial3_device =  	INIT_ESERIAL_STRUCTURE(3, "eserial2"); +#endif +#if defined(CONFIG_SYS_NS16550_COM4)  DECLARE_ESERIAL_FUNCTIONS(4);  struct serial_device eserial4_device =  	INIT_ESERIAL_STRUCTURE(4, "eserial3"); +#endif +#if defined(CONFIG_SYS_NS16550_COM5)  DECLARE_ESERIAL_FUNCTIONS(5);  struct serial_device eserial5_device =  	INIT_ESERIAL_STRUCTURE(5, "eserial4"); +#endif +#if defined(CONFIG_SYS_NS16550_COM6)  DECLARE_ESERIAL_FUNCTIONS(6);  struct serial_device eserial6_device =  	INIT_ESERIAL_STRUCTURE(6, "eserial5"); +#endif  __weak struct serial_device *default_serial_console(void)  { diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c index e47cb9a9e..148d1a6dd 100644 --- a/drivers/serial/usbtty.c +++ b/drivers/serial/usbtty.c @@ -63,7 +63,7 @@  /*   * Buffers to hold input and output data   */ -#define USBTTY_BUFFER_SIZE 256 +#define USBTTY_BUFFER_SIZE 2048  static circbuf_t usbtty_input;  static circbuf_t usbtty_output; diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 83abcbda2..b8264df3a 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -31,6 +31,7 @@ COBJS-$(CONFIG_ARMADA100_SPI) += armada100_spi.o  COBJS-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o  COBJS-$(CONFIG_ATMEL_SPI) += atmel_spi.o  COBJS-$(CONFIG_BFIN_SPI) += bfin_spi.o +COBJS-$(CONFIG_BFIN_SPI6XX) += bfin_spi6xx.o  COBJS-$(CONFIG_CF_SPI) += cf_spi.o  COBJS-$(CONFIG_CF_QSPI) += cf_qspi.o  COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o diff --git a/drivers/spi/bfin_spi6xx.c b/drivers/spi/bfin_spi6xx.c new file mode 100644 index 000000000..fde344742 --- /dev/null +++ b/drivers/spi/bfin_spi6xx.c @@ -0,0 +1,308 @@ +/* + * Analog Devices SPI3 controller driver + * + * Copyright (c) 2011 Analog Devices Inc. + * + * 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. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <common.h> +#include <malloc.h> +#include <spi.h> + +#include <asm/blackfin.h> +#include <asm/gpio.h> +#include <asm/portmux.h> +#include <asm/mach-common/bits/spi6xx.h> + +struct bfin_spi_slave { +	struct spi_slave slave; +	u32 control, clock; +	struct bfin_spi_regs *regs; +	int cs_pol; +}; + +#define to_bfin_spi_slave(s) container_of(s, struct bfin_spi_slave, slave) + +#define gpio_cs(cs) ((cs) - MAX_CTRL_CS) +#ifdef CONFIG_BFIN_SPI_GPIO_CS +# define is_gpio_cs(cs) ((cs) > MAX_CTRL_CS) +#else +# define is_gpio_cs(cs) 0 +#endif + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ +	if (is_gpio_cs(cs)) +		return gpio_is_valid(gpio_cs(cs)); +	else +		return (cs >= 1 && cs <= MAX_CTRL_CS); +} + +void spi_cs_activate(struct spi_slave *slave) +{ +	struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + +	if (is_gpio_cs(slave->cs)) { +		unsigned int cs = gpio_cs(slave->cs); +		gpio_set_value(cs, bss->cs_pol); +	} else { +		u32 ssel; +		ssel = bfin_read32(&bss->regs->ssel); +		ssel |= 1 << slave->cs; +		if (bss->cs_pol) +			ssel |= (1 << 8) << slave->cs; +		else +			ssel &= ~((1 << 8) << slave->cs); +		bfin_write32(&bss->regs->ssel, ssel); +	} + +	SSYNC(); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ +	struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + +	if (is_gpio_cs(slave->cs)) { +		unsigned int cs = gpio_cs(slave->cs); +		gpio_set_value(cs, !bss->cs_pol); +	} else { +		u32 ssel; +		ssel = bfin_read32(&bss->regs->ssel); +		if (bss->cs_pol) +			ssel &= ~((1 << 8) << slave->cs); +		else +			ssel |= (1 << 8) << slave->cs; +		/* deassert cs */ +		bfin_write32(&bss->regs->ssel, ssel); +		SSYNC(); +		/* disable cs */ +		ssel &= ~(1 << slave->cs); +		bfin_write32(&bss->regs->ssel, ssel); +	} + +	SSYNC(); +} + +void spi_init() +{ +} + +#define SPI_PINS(n) \ +	{ 0, P_SPI##n##_SCK, P_SPI##n##_MISO, P_SPI##n##_MOSI, 0 } +static unsigned short pins[][5] = { +#ifdef SPI0_REGBASE +	[0] = SPI_PINS(0), +#endif +#ifdef SPI1_REGBASE +	[1] = SPI_PINS(1), +#endif +#ifdef SPI2_REGBASE +	[2] = SPI_PINS(2), +#endif +}; + +#define SPI_CS_PINS(n) \ +	{ \ +		P_SPI##n##_SSEL1, P_SPI##n##_SSEL2, P_SPI##n##_SSEL3, \ +		P_SPI##n##_SSEL4, P_SPI##n##_SSEL5, P_SPI##n##_SSEL6, \ +		P_SPI##n##_SSEL7, \ +	} +static const unsigned short cs_pins[][7] = { +#ifdef SPI0_REGBASE +	[0] = SPI_CS_PINS(0), +#endif +#ifdef SPI1_REGBASE +	[1] = SPI_CS_PINS(1), +#endif +#ifdef SPI2_REGBASE +	[2] = SPI_CS_PINS(2), +#endif +}; + +void spi_set_speed(struct spi_slave *slave, uint hz) +{ +	struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); +	ulong sclk; +	u32 clock; + +	sclk = get_sclk1(); +	clock = sclk / hz; +	if (clock) +		clock--; +	bss->clock = clock; +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, +		unsigned int max_hz, unsigned int mode) +{ +	struct bfin_spi_slave *bss; +	u32 reg_base; + +	if (!spi_cs_is_valid(bus, cs)) +		return NULL; + +	if (bus >= ARRAY_SIZE(pins) || pins[bus] == NULL) { +		debug("%s: invalid bus %u\n", __func__, bus); +		return NULL; +	} +	switch (bus) { +#ifdef SPI0_REGBASE +	case 0: +		reg_base = SPI0_REGBASE; +		break; +#endif +#ifdef SPI1_REGBASE +	case 1: +		reg_base = SPI1_REGBASE; +		break; +#endif +#ifdef SPI2_REGBASE +	case 2: +		reg_base = SPI2_REGBASE; +		break; +#endif +	default: +		return NULL; +	} + +	bss = malloc(sizeof(*bss)); +	if (!bss) +		return NULL; + +	bss->slave.bus = bus; +	bss->slave.cs = cs; +	bss->regs = (struct bfin_spi_regs *)reg_base; +	bss->control = SPI_CTL_EN | SPI_CTL_MSTR; +	if (mode & SPI_CPHA) +		bss->control |= SPI_CTL_CPHA; +	if (mode & SPI_CPOL) +		bss->control |= SPI_CTL_CPOL; +	if (mode & SPI_LSB_FIRST) +		bss->control |= SPI_CTL_LSBF; +	bss->control &= ~SPI_CTL_ASSEL; +	bss->cs_pol = mode & SPI_CS_HIGH ? 1 : 0; +	spi_set_speed(&bss->slave, max_hz); + +	return &bss->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ +	struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); +	free(bss); +} + +int spi_claim_bus(struct spi_slave *slave) +{ +	struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + +	debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); + +	if (is_gpio_cs(slave->cs)) { +		unsigned int cs = gpio_cs(slave->cs); +		gpio_request(cs, "bfin-spi"); +		gpio_direction_output(cs, !bss->cs_pol); +		pins[slave->bus][0] = P_DONTCARE; +	} else +		pins[slave->bus][0] = cs_pins[slave->bus][slave->cs - 1]; +	peripheral_request_list(pins[slave->bus], "bfin-spi"); + +	bfin_write32(&bss->regs->control, bss->control); +	bfin_write32(&bss->regs->clock, bss->clock); +	bfin_write32(&bss->regs->delay, 0x0); +	bfin_write32(&bss->regs->rx_control, SPI_RXCTL_REN); +	bfin_write32(&bss->regs->tx_control, SPI_TXCTL_TEN | SPI_TXCTL_TTI); +	SSYNC(); + +	return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ +	struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); + +	debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); + +	peripheral_free_list(pins[slave->bus]); +	if (is_gpio_cs(slave->cs)) +		gpio_free(gpio_cs(slave->cs)); + +	bfin_write32(&bss->regs->rx_control, 0x0); +	bfin_write32(&bss->regs->tx_control, 0x0); +	bfin_write32(&bss->regs->control, 0x0); +	SSYNC(); +} + +#ifndef CONFIG_BFIN_SPI_IDLE_VAL +# define CONFIG_BFIN_SPI_IDLE_VAL 0xff +#endif + +static int spi_pio_xfer(struct bfin_spi_slave *bss, const u8 *tx, u8 *rx, +			uint bytes) +{ +	/* discard invalid rx data and empty rfifo */ +	while (!(bfin_read32(&bss->regs->status) & SPI_STAT_RFE)) +		bfin_read32(&bss->regs->rfifo); + +	while (bytes--) { +		u8 value = (tx ? *tx++ : CONFIG_BFIN_SPI_IDLE_VAL); +		debug("%s: tx:%x ", __func__, value); +		bfin_write32(&bss->regs->tfifo, value); +		SSYNC(); +		while (bfin_read32(&bss->regs->status) & SPI_STAT_RFE) +			if (ctrlc()) +				return -1; +		value = bfin_read32(&bss->regs->rfifo); +		if (rx) +			*rx++ = value; +		debug("rx:%x\n", value); +	} + +	return 0; +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, +		void *din, unsigned long flags) +{ +	struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); +	const u8 *tx = dout; +	u8 *rx = din; +	uint bytes = bitlen / 8; +	int ret = 0; + +	debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, +		slave->bus, slave->cs, bitlen, bytes, flags); + +	if (bitlen == 0) +		goto done; + +	/* we can only do 8 bit transfers */ +	if (bitlen % 8) { +		flags |= SPI_XFER_END; +		goto done; +	} + +	if (flags & SPI_XFER_BEGIN) +		spi_cs_activate(slave); + +	ret = spi_pio_xfer(bss, tx, rx, bytes); + + done: +	if (flags & SPI_XFER_END) +		spi_cs_deactivate(slave); + +	return ret; +} diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c index bb865b7f4..ffa3c1d69 100644 --- a/drivers/spi/mxs_spi.c +++ b/drivers/spi/mxs_spi.c @@ -40,17 +40,6 @@  #define MXSSSP_SMALL_TRANSFER	512 -/* - * CONFIG_MXS_SPI_DMA_ENABLE: Experimental mixed PIO/DMA support for MXS SPI - *                            host. Use with utmost caution! - * - *                            Enabling this is not yet recommended since this - *                            still doesn't support transfers to/from unaligned - *                            addresses. Therefore this driver will not work - *                            for example with saving environment. This is - *                            caused by DMA alignment constraints on MXS. - */ -  struct mxs_spi_slave {  	struct spi_slave	slave;  	uint32_t		max_khz; @@ -70,7 +59,7 @@ void spi_init(void)  int spi_cs_is_valid(unsigned int bus, unsigned int cs)  {  	/* MXS SPI: 4 ports and 3 chip selects maximum */ -	if (bus > 3 || cs > 2) +	if (!mxs_ssp_bus_id_valid(bus) || cs > 2)  		return 0;  	else  		return 1; @@ -92,7 +81,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  	if (!mxs_slave)  		return NULL; -	if (mxs_dma_init_channel(bus)) +	if (mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + bus))  		goto err_init;  	mxs_slave->slave.bus = bus; @@ -168,7 +157,12 @@ static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave,  	while (length--) {  		/* We transfer 1 byte */ +#if defined(CONFIG_MX23) +		writel(SSP_CTRL0_XFER_COUNT_MASK, &ssp_regs->hw_ssp_ctrl0_clr); +		writel(1, &ssp_regs->hw_ssp_ctrl0_set); +#elif defined(CONFIG_MX28)  		writel(1, &ssp_regs->hw_ssp_xfer_size); +#endif  		if ((flags & SPI_XFER_END) && !length)  			mxs_spi_end_xfer(ssp_regs); @@ -226,6 +220,12 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,  	int tl;  	int ret = 0; +#if defined(CONFIG_MX23) +	const int mxs_spi_pio_words = 1; +#elif defined(CONFIG_MX28) +	const int mxs_spi_pio_words = 4; +#endif +  	ALLOC_CACHE_ALIGN_BUFFER(struct mxs_dma_desc, desc, desc_count);  	memset(desc, 0, sizeof(struct mxs_dma_desc) * desc_count); @@ -281,7 +281,7 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,  		dp->cmd.data |=  			((tl & 0xffff) << MXS_DMA_DESC_BYTES_OFFSET) | -			(4 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | +			(mxs_spi_pio_words << MXS_DMA_DESC_PIO_WORDS_OFFSET) |  			MXS_DMA_DESC_HALT_ON_TERMINATE |  			MXS_DMA_DESC_TERMINATE_FLUSH; @@ -298,15 +298,19 @@ static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,  		}  		/* -		 * Write CTRL0, CMD0, CMD1, XFER_SIZE registers. It is +		 * Write CTRL0, CMD0, CMD1 and XFER_SIZE registers in +		 * case of MX28, write only CTRL0 in case of MX23 due +		 * to the difference in register layout. It is utterly  		 * essential that the XFER_SIZE register is written on  		 * a per-descriptor basis with the same size as is the  		 * descriptor!  		 */  		dp->cmd.pio_words[0] = ctrl0; +#ifdef CONFIG_MX28  		dp->cmd.pio_words[1] = 0;  		dp->cmd.pio_words[2] = 0;  		dp->cmd.pio_words[3] = tl; +#endif  		mxs_dma_desc_append(dmach, dp); @@ -332,12 +336,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,  	char dummy;  	int write = 0;  	char *data = NULL; - -#ifdef CONFIG_MXS_SPI_DMA_ENABLE  	int dma = 1; -#else -	int dma = 0; -#endif  	if (bitlen == 0) {  		if (flags & SPI_XFER_END) { diff --git a/drivers/usb/eth/smsc95xx.c b/drivers/usb/eth/smsc95xx.c index dc5ca6546..fd8f8a760 100644 --- a/drivers/usb/eth/smsc95xx.c +++ b/drivers/usb/eth/smsc95xx.c @@ -265,10 +265,6 @@ static int smsc95xx_eeprom_confirm_not_busy(struct ueth_data *dev)  	do {  		smsc95xx_read_reg(dev, E2P_CMD, &val); -		if (!(val & E2P_CMD_LOADED_)) { -			debug("No EEPROM present\n"); -			return -1; -		}  		if (!(val & E2P_CMD_BUSY_))  			return 0;  		udelay(40); diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 040eaba3b..e545b6be6 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -25,15 +25,21 @@ include $(TOPDIR)/config.mk  LIB	:= $(obj)libusb_gadget.o +# if defined(CONFIG_USB_GADGET) || defined(CONFIG_USB_ETHER) +#   Everytime you forget how crufty makefiles can get things like +#   this remind you... +ifneq (,$(CONFIG_USB_GADGET)$(CONFIG_USB_ETHER)) +COBJS-y += epautoconf.o config.o usbstring.o +endif +  # new USB gadget layer dependencies  ifdef CONFIG_USB_GADGET -COBJS-y += epautoconf.o config.o usbstring.o  COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o  COBJS-$(CONFIG_USBDOWNLOAD_GADGET) += g_dnl.o  COBJS-$(CONFIG_DFU_FUNCTION) += f_dfu.o  endif  ifdef CONFIG_USB_ETHER -COBJS-y += ether.o epautoconf.o config.o usbstring.o +COBJS-y += ether.o  COBJS-$(CONFIG_USB_ETH_RNDIS) += rndis.o  COBJS-$(CONFIG_MV_UDC)	+= mv_udc.o  COBJS-$(CONFIG_CPU_PXA25X) += pxa25x_udc.o diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index ebb5131a9..2c5600ed5 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -859,6 +859,25 @@ unknown:  			if (&f->list == &cdev->config->functions)  				f = NULL;  			break; +		/* +		 * dfu-util (version 0.5) sets bmRequestType.Receipent = Device +		 * for non-standard request (w_value = 0x21, +		 * bRequest = GET_DESCRIPTOR in this case). +		 * When only one interface is registered (as it is done now), +		 * then this request shall be handled as it was requested for +		 * interface. +		 * +		 * In the below code it is checked if only one interface is +		 * present and proper function for it is extracted. Due to that +		 * function's setup (f->setup) is called to handle this +		 * special non-standard request. +		 */ +		case USB_RECIP_DEVICE: +			debug("cdev->config->next_interface_id: %d intf: %d\n", +			       cdev->config->next_interface_id, intf); +			if (cdev->config->next_interface_id == 1) +				f = cdev->config->interface[intf]; +			break;  		}  		if (f && f->setup) diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index 10547e327..a322ae5eb 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -164,6 +164,9 @@ static void handle_getstatus(struct usb_request *req)  	/* send status response */  	dstat->bStatus = f_dfu->dfu_status; +	dstat->bwPollTimeout[0] = 0; +	dstat->bwPollTimeout[1] = 0; +	dstat->bwPollTimeout[2] = 0;  	dstat->bState = f_dfu->dfu_state;  	dstat->iString = 0;  } diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c new file mode 100644 index 000000000..c28866f7d --- /dev/null +++ b/drivers/usb/gadget/f_mass_storage.c @@ -0,0 +1,2793 @@ +/* + * f_mass_storage.c -- Mass Storage USB Composite Function + * + * Copyright (C) 2003-2008 Alan Stern + * Copyright (C) 2009 Samsung Electronics + *                    Author: Michal Nazarewicz <m.nazarewicz@samsung.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions, and the following disclaimer, + *    without modification. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + *    to endorse or promote products derived from this software without + *    specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/* + * The Mass Storage Function acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive.  In + * addition to providing an example of a genuinely useful composite + * function for a USB device, it also illustrates a technique of + * double-buffering for increased throughput. + * + * Function supports multiple logical units (LUNs).  Backing storage + * for each LUN is provided by a regular file or a block device. + * Access for each LUN can be limited to read-only.  Moreover, the + * function can indicate that LUN is removable and/or CD-ROM.  (The + * later implies read-only access.) + * + * MSF is configured by specifying a fsg_config structure.  It has the + * following fields: + * + *	nluns		Number of LUNs function have (anywhere from 1 + *				to FSG_MAX_LUNS which is 8). + *	luns		An array of LUN configuration values.  This + *				should be filled for each LUN that + *				function will include (ie. for "nluns" + *				LUNs).  Each element of the array has + *				the following fields: + *	->filename	The path to the backing file for the LUN. + *				Required if LUN is not marked as + *				removable. + *	->ro		Flag specifying access to the LUN shall be + *				read-only.  This is implied if CD-ROM + *				emulation is enabled as well as when + *				it was impossible to open "filename" + *				in R/W mode. + *	->removable	Flag specifying that LUN shall be indicated as + *				being removable. + *	->cdrom		Flag specifying that LUN shall be reported as + *				being a CD-ROM. + * + *	lun_name_format	A printf-like format for names of the LUN + *				devices.  This determines how the + *				directory in sysfs will be named. + *				Unless you are using several MSFs in + *				a single gadget (as opposed to single + *				MSF in many configurations) you may + *				leave it as NULL (in which case + *				"lun%d" will be used).  In the format + *				you can use "%d" to index LUNs for + *				MSF's with more than one LUN.  (Beware + *				that there is only one integer given + *				as an argument for the format and + *				specifying invalid format may cause + *				unspecified behaviour.) + *	thread_name	Name of the kernel thread process used by the + *				MSF.  You can safely set it to NULL + *				(in which case default "file-storage" + *				will be used). + * + *	vendor_name + *	product_name + *	release		Information used as a reply to INQUIRY + *				request.  To use default set to NULL, + *				NULL, 0xffff respectively.  The first + *				field should be 8 and the second 16 + *				characters or less. + * + *	can_stall	Set to permit function to halt bulk endpoints. + *				Disabled on some USB devices known not + *				to work correctly.  You should set it + *				to true. + * + * If "removable" is not set for a LUN then a backing file must be + * specified.  If it is set, then NULL filename means the LUN's medium + * is not loaded (an empty string as "filename" in the fsg_config + * structure causes error).  The CD-ROM emulation includes a single + * data track and no audio tracks; hence there need be only one + * backing file per LUN.  Note also that the CD-ROM block length is + * set to 512 rather than the more common value 2048. + * + * + * MSF includes support for module parameters.  If gadget using it + * decides to use it, the following module parameters will be + * available: + * + *	file=filename[,filename...] + *			Names of the files or block devices used for + *				backing storage. + *	ro=b[,b...]	Default false, boolean for read-only access. + *	removable=b[,b...] + *			Default true, boolean for removable media. + *	cdrom=b[,b...]	Default false, boolean for whether to emulate + *				a CD-ROM drive. + *	luns=N		Default N = number of filenames, number of + *				LUNs to support. + *	stall		Default determined according to the type of + *				USB device controller (usually true), + *				boolean to permit the driver to halt + *				bulk endpoints. + * + * The module parameters may be prefixed with some string.  You need + * to consult gadget's documentation or source to verify whether it is + * using those module parameters and if it does what are the prefixes + * (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is + * the prefix). + * + * + * Requirements are modest; only a bulk-in and a bulk-out endpoint are + * needed.  The memory requirement amounts to two 16K buffers, size + * configurable by a parameter.  Support is included for both + * full-speed and high-speed operation. + * + * Note that the driver is slightly non-portable in that it assumes a + * single memory/DMA buffer will be useable for bulk-in, bulk-out, and + * interrupt-in endpoints.  With most device controllers this isn't an + * issue, but there may be some with hardware restrictions that prevent + * a buffer from being used by more than one endpoint. + * + * + * The pathnames of the backing files and the ro settings are + * available in the attribute files "file" and "ro" in the lun<n> (or + * to be more precise in a directory which name comes from + * "lun_name_format" option!) subdirectory of the gadget's sysfs + * directory.  If the "removable" option is set, writing to these + * files will simulate ejecting/loading the medium (writing an empty + * line means eject) and adjusting a write-enable tab.  Changes to the + * ro setting are not allowed when the medium is loaded or if CD-ROM + * emulation is being used. + * + * When a LUN receive an "eject" SCSI request (Start/Stop Unit), + * if the LUN is removable, the backing file is released to simulate + * ejection. + * + * + * This function is heavily based on "File-backed Storage Gadget" by + * Alan Stern which in turn is heavily based on "Gadget Zero" by David + * Brownell.  The driver's SCSI command interface was based on the + * "Information technology - Small Computer System Interface - 2" + * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93, + * available at <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>. + * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which + * was based on the "Universal Serial Bus Mass Storage Class UFI + * Command Specification" document, Revision 1.0, December 14, 1998, + * available at + * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>. + */ + + +/* + *				Driver Design + * + * The MSF is fairly straightforward.  There is a main kernel + * thread that handles most of the work.  Interrupt routines field + * callbacks from the controller driver: bulk- and interrupt-request + * completion notifications, endpoint-0 events, and disconnect events. + * Completion events are passed to the main thread by wakeup calls.  Many + * ep0 requests are handled at interrupt time, but SetInterface, + * SetConfiguration, and device reset requests are forwarded to the + * thread in the form of "exceptions" using SIGUSR1 signals (since they + * should interrupt any ongoing file I/O operations). + * + * The thread's main routine implements the standard command/data/status + * parts of a SCSI interaction.  It and its subroutines are full of tests + * for pending signals/exceptions -- all this polling is necessary since + * the kernel has no setjmp/longjmp equivalents.  (Maybe this is an + * indication that the driver really wants to be running in userspace.) + * An important point is that so long as the thread is alive it keeps an + * open reference to the backing file.  This will prevent unmounting + * the backing file's underlying filesystem and could cause problems + * during system shutdown, for example.  To prevent such problems, the + * thread catches INT, TERM, and KILL signals and converts them into + * an EXIT exception. + * + * In normal operation the main thread is started during the gadget's + * fsg_bind() callback and stopped during fsg_unbind().  But it can + * also exit when it receives a signal, and there's no point leaving + * the gadget running when the thread is dead.  At of this moment, MSF + * provides no way to deregister the gadget when thread dies -- maybe + * a callback functions is needed. + * + * To provide maximum throughput, the driver uses a circular pipeline of + * buffer heads (struct fsg_buffhd).  In principle the pipeline can be + * arbitrarily long; in practice the benefits don't justify having more + * than 2 stages (i.e., double buffering).  But it helps to think of the + * pipeline as being a long one.  Each buffer head contains a bulk-in and + * a bulk-out request pointer (since the buffer can be used for both + * output and input -- directions always are given from the host's + * point of view) as well as a pointer to the buffer and various state + * variables. + * + * Use of the pipeline follows a simple protocol.  There is a variable + * (fsg->next_buffhd_to_fill) that points to the next buffer head to use. + * At any time that buffer head may still be in use from an earlier + * request, so each buffer head has a state variable indicating whether + * it is EMPTY, FULL, or BUSY.  Typical use involves waiting for the + * buffer head to be EMPTY, filling the buffer either by file I/O or by + * USB I/O (during which the buffer head is BUSY), and marking the buffer + * head FULL when the I/O is complete.  Then the buffer will be emptied + * (again possibly by USB I/O, during which it is marked BUSY) and + * finally marked EMPTY again (possibly by a completion routine). + * + * A module parameter tells the driver to avoid stalling the bulk + * endpoints wherever the transport specification allows.  This is + * necessary for some UDCs like the SuperH, which cannot reliably clear a + * halt on a bulk endpoint.  However, under certain circumstances the + * Bulk-only specification requires a stall.  In such cases the driver + * will halt the endpoint and set a flag indicating that it should clear + * the halt in software during the next device reset.  Hopefully this + * will permit everything to work correctly.  Furthermore, although the + * specification allows the bulk-out endpoint to halt when the host sends + * too much data, implementing this would cause an unavoidable race. + * The driver will always use the "no-stall" approach for OUT transfers. + * + * One subtle point concerns sending status-stage responses for ep0 + * requests.  Some of these requests, such as device reset, can involve + * interrupting an ongoing file I/O operation, which might take an + * arbitrarily long time.  During that delay the host might give up on + * the original ep0 request and issue a new one.  When that happens the + * driver should not notify the host about completion of the original + * request, as the host will no longer be waiting for it.  So the driver + * assigns to each ep0 request a unique tag, and it keeps track of the + * tag value of the request associated with a long-running exception + * (device-reset, interface-change, or configuration-change).  When the + * exception handler is finished, the status-stage response is submitted + * only if the current ep0 request tag is equal to the exception request + * tag.  Thus only the most recently received ep0 request will get a + * status-stage response. + * + * Warning: This driver source file is too long.  It ought to be split up + * into a header file plus about 3 separate .c files, to handle the details + * of the Gadget, USB Mass Storage, and SCSI protocols. + */ + +/* #define VERBOSE_DEBUG */ +/* #define DUMP_MSGS */ + +#include <config.h> +#include <malloc.h> +#include <common.h> + +#include <linux/err.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <usb_mass_storage.h> + +#include <asm/unaligned.h> +#include <linux/usb/gadget.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> +#include <usb/lin_gadget_compat.h> + +/*------------------------------------------------------------------------*/ + +#define FSG_DRIVER_DESC	"Mass Storage Function" +#define FSG_DRIVER_VERSION	"2012/06/5" + +static const char fsg_string_interface[] = "Mass Storage"; + + +#define FSG_NO_INTR_EP 1 +#define FSG_NO_DEVICE_STRINGS    1 +#define FSG_NO_OTG               1 +#define FSG_NO_INTR_EP           1 + +#include "storage_common.c" + +/*-------------------------------------------------------------------------*/ + +#define GFP_ATOMIC ((gfp_t) 0) +#define PAGE_CACHE_SHIFT	12 +#define PAGE_CACHE_SIZE		(1 << PAGE_CACHE_SHIFT) +#define kthread_create(...)	__builtin_return_address(0) +#define wait_for_completion(...) do {} while (0) + +struct kref {int x; }; +struct completion {int x; }; + +inline void set_bit(int nr, volatile void *addr) +{ +	int	mask; +	unsigned int *a = (unsigned int *) addr; + +	a += nr >> 5; +	mask = 1 << (nr & 0x1f); +	*a |= mask; +} + +inline void clear_bit(int nr, volatile void *addr) +{ +	int	mask; +	unsigned int *a = (unsigned int *) addr; + +	a += nr >> 5; +	mask = 1 << (nr & 0x1f); +	*a &= ~mask; +} + +struct fsg_dev; +struct fsg_common; + +/* Data shared by all the FSG instances. */ +struct fsg_common { +	struct usb_gadget	*gadget; +	struct fsg_dev		*fsg, *new_fsg; + +	struct usb_ep		*ep0;		/* Copy of gadget->ep0 */ +	struct usb_request	*ep0req;	/* Copy of cdev->req */ +	unsigned int		ep0_req_tag; + +	struct fsg_buffhd	*next_buffhd_to_fill; +	struct fsg_buffhd	*next_buffhd_to_drain; +	struct fsg_buffhd	buffhds[FSG_NUM_BUFFERS]; + +	int			cmnd_size; +	u8			cmnd[MAX_COMMAND_SIZE]; + +	unsigned int		nluns; +	unsigned int		lun; +	struct fsg_lun          luns[FSG_MAX_LUNS]; + +	unsigned int		bulk_out_maxpacket; +	enum fsg_state		state;		/* For exception handling */ +	unsigned int		exception_req_tag; + +	enum data_direction	data_dir; +	u32			data_size; +	u32			data_size_from_cmnd; +	u32			tag; +	u32			residue; +	u32			usb_amount_left; + +	unsigned int		can_stall:1; +	unsigned int		free_storage_on_release:1; +	unsigned int		phase_error:1; +	unsigned int		short_packet_received:1; +	unsigned int		bad_lun_okay:1; +	unsigned int		running:1; + +	int			thread_wakeup_needed; +	struct completion	thread_notifier; +	struct task_struct	*thread_task; + +	/* Callback functions. */ +	const struct fsg_operations	*ops; +	/* Gadget's private data. */ +	void			*private_data; + +	const char *vendor_name;		/*  8 characters or less */ +	const char *product_name;		/* 16 characters or less */ +	u16 release; + +	/* Vendor (8 chars), product (16 chars), release (4 +	 * hexadecimal digits) and NUL byte */ +	char inquiry_string[8 + 16 + 4 + 1]; + +	struct kref		ref; +}; + +struct fsg_config { +	unsigned nluns; +	struct fsg_lun_config { +		const char *filename; +		char ro; +		char removable; +		char cdrom; +		char nofua; +	} luns[FSG_MAX_LUNS]; + +	/* Callback functions. */ +	const struct fsg_operations     *ops; +	/* Gadget's private data. */ +	void			*private_data; + +	const char *vendor_name;		/*  8 characters or less */ +	const char *product_name;		/* 16 characters or less */ + +	char			can_stall; +}; + +struct fsg_dev { +	struct usb_function	function; +	struct usb_gadget	*gadget;	/* Copy of cdev->gadget */ +	struct fsg_common	*common; + +	u16			interface_number; + +	unsigned int		bulk_in_enabled:1; +	unsigned int		bulk_out_enabled:1; + +	unsigned long		atomic_bitflags; +#define IGNORE_BULK_OUT		0 + +	struct usb_ep		*bulk_in; +	struct usb_ep		*bulk_out; +}; + + +static inline int __fsg_is_set(struct fsg_common *common, +			       const char *func, unsigned line) +{ +	if (common->fsg) +		return 1; +	ERROR(common, "common->fsg is NULL in %s at %u\n", func, line); +	WARN_ON(1); +	return 0; +} + +#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__)) + + +static inline struct fsg_dev *fsg_from_func(struct usb_function *f) +{ +	return container_of(f, struct fsg_dev, function); +} + + +typedef void (*fsg_routine_t)(struct fsg_dev *); + +static int exception_in_progress(struct fsg_common *common) +{ +	return common->state > FSG_STATE_IDLE; +} + +/* Make bulk-out requests be divisible by the maxpacket size */ +static void set_bulk_out_req_length(struct fsg_common *common, +		struct fsg_buffhd *bh, unsigned int length) +{ +	unsigned int	rem; + +	bh->bulk_out_intended_length = length; +	rem = length % common->bulk_out_maxpacket; +	if (rem > 0) +		length += common->bulk_out_maxpacket - rem; +	bh->outreq->length = length; +} + +/*-------------------------------------------------------------------------*/ + +struct ums_board_info			*ums_info; +struct fsg_common *the_fsg_common; + +static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) +{ +	const char	*name; + +	if (ep == fsg->bulk_in) +		name = "bulk-in"; +	else if (ep == fsg->bulk_out) +		name = "bulk-out"; +	else +		name = ep->name; +	DBG(fsg, "%s set halt\n", name); +	return usb_ep_set_halt(ep); +} + +/*-------------------------------------------------------------------------*/ + +/* These routines may be called in process context or in_irq */ + +/* Caller must hold fsg->lock */ +static void wakeup_thread(struct fsg_common *common) +{ +	common->thread_wakeup_needed = 1; +} + +static void raise_exception(struct fsg_common *common, enum fsg_state new_state) +{ +	/* Do nothing if a higher-priority exception is already in progress. +	 * If a lower-or-equal priority exception is in progress, preempt it +	 * and notify the main thread by sending it a signal. */ +	if (common->state <= new_state) { +		common->exception_req_tag = common->ep0_req_tag; +		common->state = new_state; +		common->thread_wakeup_needed = 1; +	} +} + +/*-------------------------------------------------------------------------*/ + +static int ep0_queue(struct fsg_common *common) +{ +	int	rc; + +	rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC); +	common->ep0->driver_data = common; +	if (rc != 0 && rc != -ESHUTDOWN) { +		/* We can't do much more than wait for a reset */ +		WARNING(common, "error in submission: %s --> %d\n", +			common->ep0->name, rc); +	} +	return rc; +} + +/*-------------------------------------------------------------------------*/ + +/* Bulk and interrupt endpoint completion handlers. + * These always run in_irq. */ + +static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) +{ +	struct fsg_common	*common = ep->driver_data; +	struct fsg_buffhd	*bh = req->context; + +	if (req->status || req->actual != req->length) +		DBG(common, "%s --> %d, %u/%u\n", __func__, +				req->status, req->actual, req->length); +	if (req->status == -ECONNRESET)		/* Request was cancelled */ +		usb_ep_fifo_flush(ep); + +	/* Hold the lock while we update the request and buffer states */ +	bh->inreq_busy = 0; +	bh->state = BUF_STATE_EMPTY; +	wakeup_thread(common); +} + +static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) +{ +	struct fsg_common	*common = ep->driver_data; +	struct fsg_buffhd	*bh = req->context; + +	dump_msg(common, "bulk-out", req->buf, req->actual); +	if (req->status || req->actual != bh->bulk_out_intended_length) +		DBG(common, "%s --> %d, %u/%u\n", __func__, +				req->status, req->actual, +				bh->bulk_out_intended_length); +	if (req->status == -ECONNRESET)		/* Request was cancelled */ +		usb_ep_fifo_flush(ep); + +	/* Hold the lock while we update the request and buffer states */ +	bh->outreq_busy = 0; +	bh->state = BUF_STATE_FULL; +	wakeup_thread(common); +} + +/*-------------------------------------------------------------------------*/ + +/* Ep0 class-specific handlers.  These always run in_irq. */ + +static int fsg_setup(struct usb_function *f, +		const struct usb_ctrlrequest *ctrl) +{ +	struct fsg_dev		*fsg = fsg_from_func(f); +	struct usb_request	*req = fsg->common->ep0req; +	u16			w_index = le16_to_cpu(ctrl->wIndex); +	u16			w_value = le16_to_cpu(ctrl->wValue); +	u16			w_length = le16_to_cpu(ctrl->wLength); + +	if (!fsg_is_set(fsg->common)) +		return -EOPNOTSUPP; + +	switch (ctrl->bRequest) { + +	case USB_BULK_RESET_REQUEST: +		if (ctrl->bRequestType != +		    (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) +			break; +		if (w_index != fsg->interface_number || w_value != 0) +			return -EDOM; + +		/* Raise an exception to stop the current operation +		 * and reinitialize our state. */ +		DBG(fsg, "bulk reset request\n"); +		raise_exception(fsg->common, FSG_STATE_RESET); +		return DELAYED_STATUS; + +	case USB_BULK_GET_MAX_LUN_REQUEST: +		if (ctrl->bRequestType != +		    (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) +			break; +		if (w_index != fsg->interface_number || w_value != 0) +			return -EDOM; +		VDBG(fsg, "get max LUN\n"); +		*(u8 *) req->buf = fsg->common->nluns - 1; + +		/* Respond with data/status */ +		req->length = min((u16)1, w_length); +		return ep0_queue(fsg->common); +	} + +	VDBG(fsg, +	     "unknown class-specific control req " +	     "%02x.%02x v%04x i%04x l%u\n", +	     ctrl->bRequestType, ctrl->bRequest, +	     le16_to_cpu(ctrl->wValue), w_index, w_length); +	return -EOPNOTSUPP; +} + +/*-------------------------------------------------------------------------*/ + +/* All the following routines run in process context */ + +/* Use this for bulk or interrupt transfers, not ep0 */ +static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, +		struct usb_request *req, int *pbusy, +		enum fsg_buffer_state *state) +{ +	int	rc; + +	if (ep == fsg->bulk_in) +		dump_msg(fsg, "bulk-in", req->buf, req->length); + +	*pbusy = 1; +	*state = BUF_STATE_BUSY; +	rc = usb_ep_queue(ep, req, GFP_KERNEL); +	if (rc != 0) { +		*pbusy = 0; +		*state = BUF_STATE_EMPTY; + +		/* We can't do much more than wait for a reset */ + +		/* Note: currently the net2280 driver fails zero-length +		 * submissions if DMA is enabled. */ +		if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && +						req->length == 0)) +			WARNING(fsg, "error in submission: %s --> %d\n", +					ep->name, rc); +	} +} + +#define START_TRANSFER_OR(common, ep_name, req, pbusy, state)		\ +	if (fsg_is_set(common))						\ +		start_transfer((common)->fsg, (common)->fsg->ep_name,	\ +			       req, pbusy, state);			\ +	else + +#define START_TRANSFER(common, ep_name, req, pbusy, state)		\ +	START_TRANSFER_OR(common, ep_name, req, pbusy, state) (void)0 + +static void busy_indicator(void) +{ +	static int state; + +	switch (state) { +	case 0: +		puts("\r|"); break; +	case 1: +		puts("\r/"); break; +	case 2: +		puts("\r-"); break; +	case 3: +		puts("\r\\"); break; +	case 4: +		puts("\r|"); break; +	case 5: +		puts("\r/"); break; +	case 6: +		puts("\r-"); break; +	case 7: +		puts("\r\\"); break; +	default: +		state = 0; +	} +	if (state++ == 8) +		state = 0; +} + +static int sleep_thread(struct fsg_common *common) +{ +	int	rc = 0; +	int i = 0, k = 0; + +	/* Wait until a signal arrives or we are woken up */ +	for (;;) { +		if (common->thread_wakeup_needed) +			break; + +		if (++i == 50000) { +			busy_indicator(); +			i = 0; +			k++; +		} + +		usb_gadget_handle_interrupts(); +	} +	common->thread_wakeup_needed = 0; +	return rc; +} + +/*-------------------------------------------------------------------------*/ + +static int do_read(struct fsg_common *common) +{ +	struct fsg_lun		*curlun = &common->luns[common->lun]; +	u32			lba; +	struct fsg_buffhd	*bh; +	int			rc; +	u32			amount_left; +	loff_t			file_offset; +	unsigned int		amount; +	unsigned int		partial_page; +	ssize_t			nread; + +	/* Get the starting Logical Block Address and check that it's +	 * not too big */ +	if (common->cmnd[0] == SC_READ_6) +		lba = get_unaligned_be24(&common->cmnd[1]); +	else { +		lba = get_unaligned_be32(&common->cmnd[2]); + +		/* We allow DPO (Disable Page Out = don't save data in the +		 * cache) and FUA (Force Unit Access = don't read from the +		 * cache), but we don't implement them. */ +		if ((common->cmnd[1] & ~0x18) != 0) { +			curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +			return -EINVAL; +		} +	} +	if (lba >= curlun->num_sectors) { +		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; +		return -EINVAL; +	} +	file_offset = ((loff_t) lba) << 9; + +	/* Carry out the file reads */ +	amount_left = common->data_size_from_cmnd; +	if (unlikely(amount_left == 0)) +		return -EIO;		/* No default reply */ + +	for (;;) { + +		/* Figure out how much we need to read: +		 * Try to read the remaining amount. +		 * But don't read more than the buffer size. +		 * And don't try to read past the end of the file. +		 * Finally, if we're not at a page boundary, don't read past +		 *	the next page. +		 * If this means reading 0 then we were asked to read past +		 *	the end of file. */ +		amount = min(amount_left, FSG_BUFLEN); +		partial_page = file_offset & (PAGE_CACHE_SIZE - 1); +		if (partial_page > 0) +			amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - +					partial_page); + +		/* Wait for the next buffer to become available */ +		bh = common->next_buffhd_to_fill; +		while (bh->state != BUF_STATE_EMPTY) { +			rc = sleep_thread(common); +			if (rc) +				return rc; +		} + +		/* If we were asked to read past the end of file, +		 * end with an empty buffer. */ +		if (amount == 0) { +			curlun->sense_data = +					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; +			curlun->info_valid = 1; +			bh->inreq->length = 0; +			bh->state = BUF_STATE_FULL; +			break; +		} + +		/* Perform the read */ +		nread = 0; +		rc = ums_info->read_sector(&(ums_info->ums_dev), +					   file_offset / SECTOR_SIZE, +					   amount / SECTOR_SIZE, +					   (char __user *)bh->buf); +		if (rc) +			return -EIO; +		nread = amount; + +		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, +				(unsigned long long) file_offset, +				(int) nread); + +		if (nread < 0) { +			LDBG(curlun, "error in file read: %d\n", +					(int) nread); +			nread = 0; +		} else if (nread < amount) { +			LDBG(curlun, "partial file read: %d/%u\n", +					(int) nread, amount); +			nread -= (nread & 511);	/* Round down to a block */ +		} +		file_offset  += nread; +		amount_left  -= nread; +		common->residue -= nread; +		bh->inreq->length = nread; +		bh->state = BUF_STATE_FULL; + +		/* If an error occurred, report it and its position */ +		if (nread < amount) { +			curlun->sense_data = SS_UNRECOVERED_READ_ERROR; +			curlun->info_valid = 1; +			break; +		} + +		if (amount_left == 0) +			break;		/* No more left to read */ + +		/* Send this buffer and go read some more */ +		bh->inreq->zero = 0; +		START_TRANSFER_OR(common, bulk_in, bh->inreq, +			       &bh->inreq_busy, &bh->state) +			/* Don't know what to do if +			 * common->fsg is NULL */ +			return -EIO; +		common->next_buffhd_to_fill = bh->next; +	} + +	return -EIO;		/* No default reply */ +} + +/*-------------------------------------------------------------------------*/ + +static int do_write(struct fsg_common *common) +{ +	struct fsg_lun		*curlun = &common->luns[common->lun]; +	u32			lba; +	struct fsg_buffhd	*bh; +	int			get_some_more; +	u32			amount_left_to_req, amount_left_to_write; +	loff_t			usb_offset, file_offset; +	unsigned int		amount; +	unsigned int		partial_page; +	ssize_t			nwritten; +	int			rc; + +	if (curlun->ro) { +		curlun->sense_data = SS_WRITE_PROTECTED; +		return -EINVAL; +	} + +	/* Get the starting Logical Block Address and check that it's +	 * not too big */ +	if (common->cmnd[0] == SC_WRITE_6) +		lba = get_unaligned_be24(&common->cmnd[1]); +	else { +		lba = get_unaligned_be32(&common->cmnd[2]); + +		/* We allow DPO (Disable Page Out = don't save data in the +		 * cache) and FUA (Force Unit Access = write directly to the +		 * medium).  We don't implement DPO; we implement FUA by +		 * performing synchronous output. */ +		if (common->cmnd[1] & ~0x18) { +			curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +			return -EINVAL; +		} +	} +	if (lba >= curlun->num_sectors) { +		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; +		return -EINVAL; +	} + +	/* Carry out the file writes */ +	get_some_more = 1; +	file_offset = usb_offset = ((loff_t) lba) << 9; +	amount_left_to_req = common->data_size_from_cmnd; +	amount_left_to_write = common->data_size_from_cmnd; + +	while (amount_left_to_write > 0) { + +		/* Queue a request for more data from the host */ +		bh = common->next_buffhd_to_fill; +		if (bh->state == BUF_STATE_EMPTY && get_some_more) { + +			/* Figure out how much we want to get: +			 * Try to get the remaining amount. +			 * But don't get more than the buffer size. +			 * And don't try to go past the end of the file. +			 * If we're not at a page boundary, +			 *	don't go past the next page. +			 * If this means getting 0, then we were asked +			 *	to write past the end of file. +			 * Finally, round down to a block boundary. */ +			amount = min(amount_left_to_req, FSG_BUFLEN); +			partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); +			if (partial_page > 0) +				amount = min(amount, +	(unsigned int) PAGE_CACHE_SIZE - partial_page); + +			if (amount == 0) { +				get_some_more = 0; +				curlun->sense_data = +					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; +				curlun->info_valid = 1; +				continue; +			} +			amount -= (amount & 511); +			if (amount == 0) { + +				/* Why were we were asked to transfer a +				 * partial block? */ +				get_some_more = 0; +				continue; +			} + +			/* Get the next buffer */ +			usb_offset += amount; +			common->usb_amount_left -= amount; +			amount_left_to_req -= amount; +			if (amount_left_to_req == 0) +				get_some_more = 0; + +			/* amount is always divisible by 512, hence by +			 * the bulk-out maxpacket size */ +			bh->outreq->length = amount; +			bh->bulk_out_intended_length = amount; +			bh->outreq->short_not_ok = 1; +			START_TRANSFER_OR(common, bulk_out, bh->outreq, +					  &bh->outreq_busy, &bh->state) +				/* Don't know what to do if +				 * common->fsg is NULL */ +				return -EIO; +			common->next_buffhd_to_fill = bh->next; +			continue; +		} + +		/* Write the received data to the backing file */ +		bh = common->next_buffhd_to_drain; +		if (bh->state == BUF_STATE_EMPTY && !get_some_more) +			break;			/* We stopped early */ +		if (bh->state == BUF_STATE_FULL) { +			common->next_buffhd_to_drain = bh->next; +			bh->state = BUF_STATE_EMPTY; + +			/* Did something go wrong with the transfer? */ +			if (bh->outreq->status != 0) { +				curlun->sense_data = SS_COMMUNICATION_FAILURE; +				curlun->info_valid = 1; +				break; +			} + +			amount = bh->outreq->actual; + +			/* Perform the write */ +			rc = ums_info->write_sector(&(ums_info->ums_dev), +					       file_offset / SECTOR_SIZE, +					       amount / SECTOR_SIZE, +					       (char __user *)bh->buf); +			if (rc) +				return -EIO; +			nwritten = amount; + +			VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, +					(unsigned long long) file_offset, +					(int) nwritten); + +			if (nwritten < 0) { +				LDBG(curlun, "error in file write: %d\n", +						(int) nwritten); +				nwritten = 0; +			} else if (nwritten < amount) { +				LDBG(curlun, "partial file write: %d/%u\n", +						(int) nwritten, amount); +				nwritten -= (nwritten & 511); +				/* Round down to a block */ +			} +			file_offset += nwritten; +			amount_left_to_write -= nwritten; +			common->residue -= nwritten; + +			/* If an error occurred, report it and its position */ +			if (nwritten < amount) { +				curlun->sense_data = SS_WRITE_ERROR; +				curlun->info_valid = 1; +				break; +			} + +			/* Did the host decide to stop early? */ +			if (bh->outreq->actual != bh->outreq->length) { +				common->short_packet_received = 1; +				break; +			} +			continue; +		} + +		/* Wait for something to happen */ +		rc = sleep_thread(common); +		if (rc) +			return rc; +	} + +	return -EIO;		/* No default reply */ +} + +/*-------------------------------------------------------------------------*/ + +static int do_synchronize_cache(struct fsg_common *common) +{ +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int do_verify(struct fsg_common *common) +{ +	struct fsg_lun		*curlun = &common->luns[common->lun]; +	u32			lba; +	u32			verification_length; +	struct fsg_buffhd	*bh = common->next_buffhd_to_fill; +	loff_t			file_offset; +	u32			amount_left; +	unsigned int		amount; +	ssize_t			nread; +	int			rc; + +	/* Get the starting Logical Block Address and check that it's +	 * not too big */ +	lba = get_unaligned_be32(&common->cmnd[2]); +	if (lba >= curlun->num_sectors) { +		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; +		return -EINVAL; +	} + +	/* We allow DPO (Disable Page Out = don't save data in the +	 * cache) but we don't implement it. */ +	if (common->cmnd[1] & ~0x10) { +		curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +		return -EINVAL; +	} + +	verification_length = get_unaligned_be16(&common->cmnd[7]); +	if (unlikely(verification_length == 0)) +		return -EIO;		/* No default reply */ + +	/* Prepare to carry out the file verify */ +	amount_left = verification_length << 9; +	file_offset = ((loff_t) lba) << 9; + +	/* Write out all the dirty buffers before invalidating them */ + +	/* Just try to read the requested blocks */ +	while (amount_left > 0) { + +		/* Figure out how much we need to read: +		 * Try to read the remaining amount, but not more than +		 * the buffer size. +		 * And don't try to read past the end of the file. +		 * If this means reading 0 then we were asked to read +		 * past the end of file. */ +		amount = min(amount_left, FSG_BUFLEN); +		if (amount == 0) { +			curlun->sense_data = +					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; +			curlun->info_valid = 1; +			break; +		} + +		/* Perform the read */ +		nread = 0; +		rc = ums_info->read_sector(&(ums_info->ums_dev), +					   file_offset / SECTOR_SIZE, +					   amount / SECTOR_SIZE, +					   (char __user *)bh->buf); +		if (rc) +			return -EIO; +		nread = amount; + +		VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, +				(unsigned long long) file_offset, +				(int) nread); +		if (nread < 0) { +			LDBG(curlun, "error in file verify: %d\n", +					(int) nread); +			nread = 0; +		} else if (nread < amount) { +			LDBG(curlun, "partial file verify: %d/%u\n", +					(int) nread, amount); +			nread -= (nread & 511);	/* Round down to a sector */ +		} +		if (nread == 0) { +			curlun->sense_data = SS_UNRECOVERED_READ_ERROR; +			curlun->info_valid = 1; +			break; +		} +		file_offset += nread; +		amount_left -= nread; +	} +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) +{ +	struct fsg_lun *curlun = &common->luns[common->lun]; +	static const char vendor_id[] = "Linux   "; +	u8	*buf = (u8 *) bh->buf; + +	if (!curlun) {		/* Unsupported LUNs are okay */ +		common->bad_lun_okay = 1; +		memset(buf, 0, 36); +		buf[0] = 0x7f;		/* Unsupported, no device-type */ +		buf[4] = 31;		/* Additional length */ +		return 36; +	} + +	memset(buf, 0, 8); +	buf[0] = TYPE_DISK; +	buf[2] = 2;		/* ANSI SCSI level 2 */ +	buf[3] = 2;		/* SCSI-2 INQUIRY data format */ +	buf[4] = 31;		/* Additional length */ +				/* No special options */ +	sprintf((char *) (buf + 8), "%-8s%-16s%04x", (char*) vendor_id , +			ums_info->name, (u16) 0xffff); + +	return 36; +} + + +static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; +	u8		*buf = (u8 *) bh->buf; +	u32		sd, sdinfo; +	int		valid; + +	/* +	 * From the SCSI-2 spec., section 7.9 (Unit attention condition): +	 * +	 * If a REQUEST SENSE command is received from an initiator +	 * with a pending unit attention condition (before the target +	 * generates the contingent allegiance condition), then the +	 * target shall either: +	 *   a) report any pending sense data and preserve the unit +	 *	attention condition on the logical unit, or, +	 *   b) report the unit attention condition, may discard any +	 *	pending sense data, and clear the unit attention +	 *	condition on the logical unit for that initiator. +	 * +	 * FSG normally uses option a); enable this code to use option b). +	 */ +#if 0 +	if (curlun && curlun->unit_attention_data != SS_NO_SENSE) { +		curlun->sense_data = curlun->unit_attention_data; +		curlun->unit_attention_data = SS_NO_SENSE; +	} +#endif + +	if (!curlun) {		/* Unsupported LUNs are okay */ +		common->bad_lun_okay = 1; +		sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; +		sdinfo = 0; +		valid = 0; +	} else { +		sd = curlun->sense_data; +		valid = curlun->info_valid << 7; +		curlun->sense_data = SS_NO_SENSE; +		curlun->info_valid = 0; +	} + +	memset(buf, 0, 18); +	buf[0] = valid | 0x70;			/* Valid, current error */ +	buf[2] = SK(sd); +	put_unaligned_be32(sdinfo, &buf[3]);	/* Sense information */ +	buf[7] = 18 - 8;			/* Additional sense length */ +	buf[12] = ASC(sd); +	buf[13] = ASCQ(sd); +	return 18; +} + +static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; +	u32		lba = get_unaligned_be32(&common->cmnd[2]); +	int		pmi = common->cmnd[8]; +	u8		*buf = (u8 *) bh->buf; + +	/* Check the PMI and LBA fields */ +	if (pmi > 1 || (pmi == 0 && lba != 0)) { +		curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +		return -EINVAL; +	} + +	put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); +						/* Max logical block */ +	put_unaligned_be32(512, &buf[4]);	/* Block length */ +	return 8; +} + +static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; +	int		msf = common->cmnd[1] & 0x02; +	u32		lba = get_unaligned_be32(&common->cmnd[2]); +	u8		*buf = (u8 *) bh->buf; + +	if (common->cmnd[1] & ~0x02) {		/* Mask away MSF */ +		curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +		return -EINVAL; +	} +	if (lba >= curlun->num_sectors) { +		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; +		return -EINVAL; +	} + +	memset(buf, 0, 8); +	buf[0] = 0x01;		/* 2048 bytes of user data, rest is EC */ +	store_cdrom_address(&buf[4], msf, lba); +	return 8; +} + + +static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; +	int		msf = common->cmnd[1] & 0x02; +	int		start_track = common->cmnd[6]; +	u8		*buf = (u8 *) bh->buf; + +	if ((common->cmnd[1] & ~0x02) != 0 ||	/* Mask away MSF */ +			start_track > 1) { +		curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +		return -EINVAL; +	} + +	memset(buf, 0, 20); +	buf[1] = (20-2);		/* TOC data length */ +	buf[2] = 1;			/* First track number */ +	buf[3] = 1;			/* Last track number */ +	buf[5] = 0x16;			/* Data track, copying allowed */ +	buf[6] = 0x01;			/* Only track is number 1 */ +	store_cdrom_address(&buf[8], msf, 0); + +	buf[13] = 0x16;			/* Lead-out track is data */ +	buf[14] = 0xAA;			/* Lead-out track number */ +	store_cdrom_address(&buf[16], msf, curlun->num_sectors); + +	return 20; +} + +static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; +	int		mscmnd = common->cmnd[0]; +	u8		*buf = (u8 *) bh->buf; +	u8		*buf0 = buf; +	int		pc, page_code; +	int		changeable_values, all_pages; +	int		valid_page = 0; +	int		len, limit; + +	if ((common->cmnd[1] & ~0x08) != 0) {	/* Mask away DBD */ +		curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +		return -EINVAL; +	} +	pc = common->cmnd[2] >> 6; +	page_code = common->cmnd[2] & 0x3f; +	if (pc == 3) { +		curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; +		return -EINVAL; +	} +	changeable_values = (pc == 1); +	all_pages = (page_code == 0x3f); + +	/* Write the mode parameter header.  Fixed values are: default +	 * medium type, no cache control (DPOFUA), and no block descriptors. +	 * The only variable value is the WriteProtect bit.  We will fill in +	 * the mode data length later. */ +	memset(buf, 0, 8); +	if (mscmnd == SC_MODE_SENSE_6) { +		buf[2] = (curlun->ro ? 0x80 : 0x00);		/* WP, DPOFUA */ +		buf += 4; +		limit = 255; +	} else {			/* SC_MODE_SENSE_10 */ +		buf[3] = (curlun->ro ? 0x80 : 0x00);		/* WP, DPOFUA */ +		buf += 8; +		limit = 65535;		/* Should really be FSG_BUFLEN */ +	} + +	/* No block descriptors */ + +	/* The mode pages, in numerical order.  The only page we support +	 * is the Caching page. */ +	if (page_code == 0x08 || all_pages) { +		valid_page = 1; +		buf[0] = 0x08;		/* Page code */ +		buf[1] = 10;		/* Page length */ +		memset(buf+2, 0, 10);	/* None of the fields are changeable */ + +		if (!changeable_values) { +			buf[2] = 0x04;	/* Write cache enable, */ +					/* Read cache not disabled */ +					/* No cache retention priorities */ +			put_unaligned_be16(0xffff, &buf[4]); +					/* Don't disable prefetch */ +					/* Minimum prefetch = 0 */ +			put_unaligned_be16(0xffff, &buf[8]); +					/* Maximum prefetch */ +			put_unaligned_be16(0xffff, &buf[10]); +					/* Maximum prefetch ceiling */ +		} +		buf += 12; +	} + +	/* Check that a valid page was requested and the mode data length +	 * isn't too long. */ +	len = buf - buf0; +	if (!valid_page || len > limit) { +		curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +		return -EINVAL; +	} + +	/*  Store the mode data length */ +	if (mscmnd == SC_MODE_SENSE_6) +		buf0[0] = len - 1; +	else +		put_unaligned_be16(len - 2, buf0); +	return len; +} + + +static int do_start_stop(struct fsg_common *common) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; + +	if (!curlun) { +		return -EINVAL; +	} else if (!curlun->removable) { +		curlun->sense_data = SS_INVALID_COMMAND; +		return -EINVAL; +	} + +	return 0; +} + +static int do_prevent_allow(struct fsg_common *common) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; +	int		prevent; + +	if (!curlun->removable) { +		curlun->sense_data = SS_INVALID_COMMAND; +		return -EINVAL; +	} + +	prevent = common->cmnd[4] & 0x01; +	if ((common->cmnd[4] & ~0x01) != 0) {	/* Mask away Prevent */ +		curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +		return -EINVAL; +	} + +	if (curlun->prevent_medium_removal && !prevent) +		fsg_lun_fsync_sub(curlun); +	curlun->prevent_medium_removal = prevent; +	return 0; +} + + +static int do_read_format_capacities(struct fsg_common *common, +			struct fsg_buffhd *bh) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; +	u8		*buf = (u8 *) bh->buf; + +	buf[0] = buf[1] = buf[2] = 0; +	buf[3] = 8;	/* Only the Current/Maximum Capacity Descriptor */ +	buf += 4; + +	put_unaligned_be32(curlun->num_sectors, &buf[0]); +						/* Number of blocks */ +	put_unaligned_be32(512, &buf[4]);	/* Block length */ +	buf[4] = 0x02;				/* Current capacity */ +	return 12; +} + + +static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh) +{ +	struct fsg_lun	*curlun = &common->luns[common->lun]; + +	/* We don't support MODE SELECT */ +	if (curlun) +		curlun->sense_data = SS_INVALID_COMMAND; +	return -EINVAL; +} + + +/*-------------------------------------------------------------------------*/ + +static int halt_bulk_in_endpoint(struct fsg_dev *fsg) +{ +	int	rc; + +	rc = fsg_set_halt(fsg, fsg->bulk_in); +	if (rc == -EAGAIN) +		VDBG(fsg, "delayed bulk-in endpoint halt\n"); +	while (rc != 0) { +		if (rc != -EAGAIN) { +			WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); +			rc = 0; +			break; +		} + +		rc = usb_ep_set_halt(fsg->bulk_in); +	} +	return rc; +} + +static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) +{ +	int	rc; + +	DBG(fsg, "bulk-in set wedge\n"); +	rc = 0; /* usb_ep_set_wedge(fsg->bulk_in); */ +	if (rc == -EAGAIN) +		VDBG(fsg, "delayed bulk-in endpoint wedge\n"); +	while (rc != 0) { +		if (rc != -EAGAIN) { +			WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); +			rc = 0; +			break; +		} +	} +	return rc; +} + +static int pad_with_zeros(struct fsg_dev *fsg) +{ +	struct fsg_buffhd	*bh = fsg->common->next_buffhd_to_fill; +	u32			nkeep = bh->inreq->length; +	u32			nsend; +	int			rc; + +	bh->state = BUF_STATE_EMPTY;		/* For the first iteration */ +	fsg->common->usb_amount_left = nkeep + fsg->common->residue; +	while (fsg->common->usb_amount_left > 0) { + +		/* Wait for the next buffer to be free */ +		while (bh->state != BUF_STATE_EMPTY) { +			rc = sleep_thread(fsg->common); +			if (rc) +				return rc; +		} + +		nsend = min(fsg->common->usb_amount_left, FSG_BUFLEN); +		memset(bh->buf + nkeep, 0, nsend - nkeep); +		bh->inreq->length = nsend; +		bh->inreq->zero = 0; +		start_transfer(fsg, fsg->bulk_in, bh->inreq, +				&bh->inreq_busy, &bh->state); +		bh = fsg->common->next_buffhd_to_fill = bh->next; +		fsg->common->usb_amount_left -= nsend; +		nkeep = 0; +	} +	return 0; +} + +static int throw_away_data(struct fsg_common *common) +{ +	struct fsg_buffhd	*bh; +	u32			amount; +	int			rc; + +	for (bh = common->next_buffhd_to_drain; +	     bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0; +	     bh = common->next_buffhd_to_drain) { + +		/* Throw away the data in a filled buffer */ +		if (bh->state == BUF_STATE_FULL) { +			bh->state = BUF_STATE_EMPTY; +			common->next_buffhd_to_drain = bh->next; + +			/* A short packet or an error ends everything */ +			if (bh->outreq->actual != bh->outreq->length || +					bh->outreq->status != 0) { +				raise_exception(common, +						FSG_STATE_ABORT_BULK_OUT); +				return -EINTR; +			} +			continue; +		} + +		/* Try to submit another request if we need one */ +		bh = common->next_buffhd_to_fill; +		if (bh->state == BUF_STATE_EMPTY +		 && common->usb_amount_left > 0) { +			amount = min(common->usb_amount_left, FSG_BUFLEN); + +			/* amount is always divisible by 512, hence by +			 * the bulk-out maxpacket size */ +			bh->outreq->length = amount; +			bh->bulk_out_intended_length = amount; +			bh->outreq->short_not_ok = 1; +			START_TRANSFER_OR(common, bulk_out, bh->outreq, +					  &bh->outreq_busy, &bh->state) +				/* Don't know what to do if +				 * common->fsg is NULL */ +				return -EIO; +			common->next_buffhd_to_fill = bh->next; +			common->usb_amount_left -= amount; +			continue; +		} + +		/* Otherwise wait for something to happen */ +		rc = sleep_thread(common); +		if (rc) +			return rc; +	} +	return 0; +} + + +static int finish_reply(struct fsg_common *common) +{ +	struct fsg_buffhd	*bh = common->next_buffhd_to_fill; +	int			rc = 0; + +	switch (common->data_dir) { +	case DATA_DIR_NONE: +		break;			/* Nothing to send */ + +	/* If we don't know whether the host wants to read or write, +	 * this must be CB or CBI with an unknown command.  We mustn't +	 * try to send or receive any data.  So stall both bulk pipes +	 * if we can and wait for a reset. */ +	case DATA_DIR_UNKNOWN: +		if (!common->can_stall) { +			/* Nothing */ +		} else if (fsg_is_set(common)) { +			fsg_set_halt(common->fsg, common->fsg->bulk_out); +			rc = halt_bulk_in_endpoint(common->fsg); +		} else { +			/* Don't know what to do if common->fsg is NULL */ +			rc = -EIO; +		} +		break; + +	/* All but the last buffer of data must have already been sent */ +	case DATA_DIR_TO_HOST: +		if (common->data_size == 0) { +			/* Nothing to send */ + +		/* If there's no residue, simply send the last buffer */ +		} else if (common->residue == 0) { +			bh->inreq->zero = 0; +			START_TRANSFER_OR(common, bulk_in, bh->inreq, +					  &bh->inreq_busy, &bh->state) +				return -EIO; +			common->next_buffhd_to_fill = bh->next; + +		/* For Bulk-only, if we're allowed to stall then send the +		 * short packet and halt the bulk-in endpoint.  If we can't +		 * stall, pad out the remaining data with 0's. */ +		} else if (common->can_stall) { +			bh->inreq->zero = 1; +			START_TRANSFER_OR(common, bulk_in, bh->inreq, +					  &bh->inreq_busy, &bh->state) +				/* Don't know what to do if +				 * common->fsg is NULL */ +				rc = -EIO; +			common->next_buffhd_to_fill = bh->next; +			if (common->fsg) +				rc = halt_bulk_in_endpoint(common->fsg); +		} else if (fsg_is_set(common)) { +			rc = pad_with_zeros(common->fsg); +		} else { +			/* Don't know what to do if common->fsg is NULL */ +			rc = -EIO; +		} +		break; + +	/* We have processed all we want from the data the host has sent. +	 * There may still be outstanding bulk-out requests. */ +	case DATA_DIR_FROM_HOST: +		if (common->residue == 0) { +			/* Nothing to receive */ + +		/* Did the host stop sending unexpectedly early? */ +		} else if (common->short_packet_received) { +			raise_exception(common, FSG_STATE_ABORT_BULK_OUT); +			rc = -EINTR; + +		/* We haven't processed all the incoming data.  Even though +		 * we may be allowed to stall, doing so would cause a race. +		 * The controller may already have ACK'ed all the remaining +		 * bulk-out packets, in which case the host wouldn't see a +		 * STALL.  Not realizing the endpoint was halted, it wouldn't +		 * clear the halt -- leading to problems later on. */ +#if 0 +		} else if (common->can_stall) { +			if (fsg_is_set(common)) +				fsg_set_halt(common->fsg, +					     common->fsg->bulk_out); +			raise_exception(common, FSG_STATE_ABORT_BULK_OUT); +			rc = -EINTR; +#endif + +		/* We can't stall.  Read in the excess data and throw it +		 * all away. */ +		} else { +			rc = throw_away_data(common); +		} +		break; +	} +	return rc; +} + + +static int send_status(struct fsg_common *common) +{ +	struct fsg_lun		*curlun = &common->luns[common->lun]; +	struct fsg_buffhd	*bh; +	struct bulk_cs_wrap	*csw; +	int			rc; +	u8			status = USB_STATUS_PASS; +	u32			sd, sdinfo = 0; + +	/* Wait for the next buffer to become available */ +	bh = common->next_buffhd_to_fill; +	while (bh->state != BUF_STATE_EMPTY) { +		rc = sleep_thread(common); +		if (rc) +			return rc; +	} + +	if (curlun) +		sd = curlun->sense_data; +	else if (common->bad_lun_okay) +		sd = SS_NO_SENSE; +	else +		sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + +	if (common->phase_error) { +		DBG(common, "sending phase-error status\n"); +		status = USB_STATUS_PHASE_ERROR; +		sd = SS_INVALID_COMMAND; +	} else if (sd != SS_NO_SENSE) { +		DBG(common, "sending command-failure status\n"); +		status = USB_STATUS_FAIL; +		VDBG(common, "  sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" +			"  info x%x\n", +			SK(sd), ASC(sd), ASCQ(sd), sdinfo); +	} + +	/* Store and send the Bulk-only CSW */ +	csw = (void *)bh->buf; + +	csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); +	csw->Tag = common->tag; +	csw->Residue = cpu_to_le32(common->residue); +	csw->Status = status; + +	bh->inreq->length = USB_BULK_CS_WRAP_LEN; +	bh->inreq->zero = 0; +	START_TRANSFER_OR(common, bulk_in, bh->inreq, +			  &bh->inreq_busy, &bh->state) +		/* Don't know what to do if common->fsg is NULL */ +		return -EIO; + +	common->next_buffhd_to_fill = bh->next; +	return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* Check whether the command is properly formed and whether its data size + * and direction agree with the values we already have. */ +static int check_command(struct fsg_common *common, int cmnd_size, +		enum data_direction data_dir, unsigned int mask, +		int needs_medium, const char *name) +{ +	int			i; +	int			lun = common->cmnd[1] >> 5; +	static const char	dirletter[4] = {'u', 'o', 'i', 'n'}; +	char			hdlen[20]; +	struct fsg_lun		*curlun; + +	hdlen[0] = 0; +	if (common->data_dir != DATA_DIR_UNKNOWN) +		sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir], +				common->data_size); +	VDBG(common, "SCSI command: %s;  Dc=%d, D%c=%u;  Hc=%d%s\n", +	     name, cmnd_size, dirletter[(int) data_dir], +	     common->data_size_from_cmnd, common->cmnd_size, hdlen); + +	/* We can't reply at all until we know the correct data direction +	 * and size. */ +	if (common->data_size_from_cmnd == 0) +		data_dir = DATA_DIR_NONE; +	if (common->data_size < common->data_size_from_cmnd) { +		/* Host data size < Device data size is a phase error. +		 * Carry out the command, but only transfer as much as +		 * we are allowed. */ +		common->data_size_from_cmnd = common->data_size; +		common->phase_error = 1; +	} +	common->residue = common->data_size; +	common->usb_amount_left = common->data_size; + +	/* Conflicting data directions is a phase error */ +	if (common->data_dir != data_dir +	 && common->data_size_from_cmnd > 0) { +		common->phase_error = 1; +		return -EINVAL; +	} + +	/* Verify the length of the command itself */ +	if (cmnd_size != common->cmnd_size) { + +		/* Special case workaround: There are plenty of buggy SCSI +		 * implementations. Many have issues with cbw->Length +		 * field passing a wrong command size. For those cases we +		 * always try to work around the problem by using the length +		 * sent by the host side provided it is at least as large +		 * as the correct command length. +		 * Examples of such cases would be MS-Windows, which issues +		 * REQUEST SENSE with cbw->Length == 12 where it should +		 * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and +		 * REQUEST SENSE with cbw->Length == 10 where it should +		 * be 6 as well. +		 */ +		if (cmnd_size <= common->cmnd_size) { +			DBG(common, "%s is buggy! Expected length %d " +			    "but we got %d\n", name, +			    cmnd_size, common->cmnd_size); +			cmnd_size = common->cmnd_size; +		} else { +			common->phase_error = 1; +			return -EINVAL; +		} +	} + +	/* Check that the LUN values are consistent */ +	if (common->lun != lun) +		DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n", +		    common->lun, lun); + +	/* Check the LUN */ +	if (common->lun >= 0 && common->lun < common->nluns) { +		curlun = &common->luns[common->lun]; +		if (common->cmnd[0] != SC_REQUEST_SENSE) { +			curlun->sense_data = SS_NO_SENSE; +			curlun->info_valid = 0; +		} +	} else { +		curlun = NULL; +		common->bad_lun_okay = 0; + +		/* INQUIRY and REQUEST SENSE commands are explicitly allowed +		 * to use unsupported LUNs; all others may not. */ +		if (common->cmnd[0] != SC_INQUIRY && +		    common->cmnd[0] != SC_REQUEST_SENSE) { +			DBG(common, "unsupported LUN %d\n", common->lun); +			return -EINVAL; +		} +	} +#if 0 +	/* If a unit attention condition exists, only INQUIRY and +	 * REQUEST SENSE commands are allowed; anything else must fail. */ +	if (curlun && curlun->unit_attention_data != SS_NO_SENSE && +			common->cmnd[0] != SC_INQUIRY && +			common->cmnd[0] != SC_REQUEST_SENSE) { +		curlun->sense_data = curlun->unit_attention_data; +		curlun->unit_attention_data = SS_NO_SENSE; +		return -EINVAL; +	} +#endif +	/* Check that only command bytes listed in the mask are non-zero */ +	common->cmnd[1] &= 0x1f;			/* Mask away the LUN */ +	for (i = 1; i < cmnd_size; ++i) { +		if (common->cmnd[i] && !(mask & (1 << i))) { +			if (curlun) +				curlun->sense_data = SS_INVALID_FIELD_IN_CDB; +			return -EINVAL; +		} +	} + +	return 0; +} + + +static int do_scsi_command(struct fsg_common *common) +{ +	struct fsg_buffhd	*bh; +	int			rc; +	int			reply = -EINVAL; +	int			i; +	static char		unknown[16]; +	struct fsg_lun		*curlun = &common->luns[common->lun]; + +	dump_cdb(common); + +	/* Wait for the next buffer to become available for data or status */ +	bh = common->next_buffhd_to_fill; +	common->next_buffhd_to_drain = bh; +	while (bh->state != BUF_STATE_EMPTY) { +		rc = sleep_thread(common); +		if (rc) +			return rc; +	} +	common->phase_error = 0; +	common->short_packet_received = 0; + +	down_read(&common->filesem);	/* We're using the backing file */ +	switch (common->cmnd[0]) { + +	case SC_INQUIRY: +		common->data_size_from_cmnd = common->cmnd[4]; +		reply = check_command(common, 6, DATA_DIR_TO_HOST, +				      (1<<4), 0, +				      "INQUIRY"); +		if (reply == 0) +			reply = do_inquiry(common, bh); +		break; + +	case SC_MODE_SELECT_6: +		common->data_size_from_cmnd = common->cmnd[4]; +		reply = check_command(common, 6, DATA_DIR_FROM_HOST, +				      (1<<1) | (1<<4), 0, +				      "MODE SELECT(6)"); +		if (reply == 0) +			reply = do_mode_select(common, bh); +		break; + +	case SC_MODE_SELECT_10: +		common->data_size_from_cmnd = +			get_unaligned_be16(&common->cmnd[7]); +		reply = check_command(common, 10, DATA_DIR_FROM_HOST, +				      (1<<1) | (3<<7), 0, +				      "MODE SELECT(10)"); +		if (reply == 0) +			reply = do_mode_select(common, bh); +		break; + +	case SC_MODE_SENSE_6: +		common->data_size_from_cmnd = common->cmnd[4]; +		reply = check_command(common, 6, DATA_DIR_TO_HOST, +				      (1<<1) | (1<<2) | (1<<4), 0, +				      "MODE SENSE(6)"); +		if (reply == 0) +			reply = do_mode_sense(common, bh); +		break; + +	case SC_MODE_SENSE_10: +		common->data_size_from_cmnd = +			get_unaligned_be16(&common->cmnd[7]); +		reply = check_command(common, 10, DATA_DIR_TO_HOST, +				      (1<<1) | (1<<2) | (3<<7), 0, +				      "MODE SENSE(10)"); +		if (reply == 0) +			reply = do_mode_sense(common, bh); +		break; + +	case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: +		common->data_size_from_cmnd = 0; +		reply = check_command(common, 6, DATA_DIR_NONE, +				      (1<<4), 0, +				      "PREVENT-ALLOW MEDIUM REMOVAL"); +		if (reply == 0) +			reply = do_prevent_allow(common); +		break; + +	case SC_READ_6: +		i = common->cmnd[4]; +		common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; +		reply = check_command(common, 6, DATA_DIR_TO_HOST, +				      (7<<1) | (1<<4), 1, +				      "READ(6)"); +		if (reply == 0) +			reply = do_read(common); +		break; + +	case SC_READ_10: +		common->data_size_from_cmnd = +				get_unaligned_be16(&common->cmnd[7]) << 9; +		reply = check_command(common, 10, DATA_DIR_TO_HOST, +				      (1<<1) | (0xf<<2) | (3<<7), 1, +				      "READ(10)"); +		if (reply == 0) +			reply = do_read(common); +		break; + +	case SC_READ_12: +		common->data_size_from_cmnd = +				get_unaligned_be32(&common->cmnd[6]) << 9; +		reply = check_command(common, 12, DATA_DIR_TO_HOST, +				      (1<<1) | (0xf<<2) | (0xf<<6), 1, +				      "READ(12)"); +		if (reply == 0) +			reply = do_read(common); +		break; + +	case SC_READ_CAPACITY: +		common->data_size_from_cmnd = 8; +		reply = check_command(common, 10, DATA_DIR_TO_HOST, +				      (0xf<<2) | (1<<8), 1, +				      "READ CAPACITY"); +		if (reply == 0) +			reply = do_read_capacity(common, bh); +		break; + +	case SC_READ_HEADER: +		if (!common->luns[common->lun].cdrom) +			goto unknown_cmnd; +		common->data_size_from_cmnd = +			get_unaligned_be16(&common->cmnd[7]); +		reply = check_command(common, 10, DATA_DIR_TO_HOST, +				      (3<<7) | (0x1f<<1), 1, +				      "READ HEADER"); +		if (reply == 0) +			reply = do_read_header(common, bh); +		break; + +	case SC_READ_TOC: +		if (!common->luns[common->lun].cdrom) +			goto unknown_cmnd; +		common->data_size_from_cmnd = +			get_unaligned_be16(&common->cmnd[7]); +		reply = check_command(common, 10, DATA_DIR_TO_HOST, +				      (7<<6) | (1<<1), 1, +				      "READ TOC"); +		if (reply == 0) +			reply = do_read_toc(common, bh); +		break; + +	case SC_READ_FORMAT_CAPACITIES: +		common->data_size_from_cmnd = +			get_unaligned_be16(&common->cmnd[7]); +		reply = check_command(common, 10, DATA_DIR_TO_HOST, +				      (3<<7), 1, +				      "READ FORMAT CAPACITIES"); +		if (reply == 0) +			reply = do_read_format_capacities(common, bh); +		break; + +	case SC_REQUEST_SENSE: +		common->data_size_from_cmnd = common->cmnd[4]; +		reply = check_command(common, 6, DATA_DIR_TO_HOST, +				      (1<<4), 0, +				      "REQUEST SENSE"); +		if (reply == 0) +			reply = do_request_sense(common, bh); +		break; + +	case SC_START_STOP_UNIT: +		common->data_size_from_cmnd = 0; +		reply = check_command(common, 6, DATA_DIR_NONE, +				      (1<<1) | (1<<4), 0, +				      "START-STOP UNIT"); +		if (reply == 0) +			reply = do_start_stop(common); +		break; + +	case SC_SYNCHRONIZE_CACHE: +		common->data_size_from_cmnd = 0; +		reply = check_command(common, 10, DATA_DIR_NONE, +				      (0xf<<2) | (3<<7), 1, +				      "SYNCHRONIZE CACHE"); +		if (reply == 0) +			reply = do_synchronize_cache(common); +		break; + +	case SC_TEST_UNIT_READY: +		common->data_size_from_cmnd = 0; +		reply = check_command(common, 6, DATA_DIR_NONE, +				0, 1, +				"TEST UNIT READY"); +		break; + +	/* Although optional, this command is used by MS-Windows.  We +	 * support a minimal version: BytChk must be 0. */ +	case SC_VERIFY: +		common->data_size_from_cmnd = 0; +		reply = check_command(common, 10, DATA_DIR_NONE, +				      (1<<1) | (0xf<<2) | (3<<7), 1, +				      "VERIFY"); +		if (reply == 0) +			reply = do_verify(common); +		break; + +	case SC_WRITE_6: +		i = common->cmnd[4]; +		common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; +		reply = check_command(common, 6, DATA_DIR_FROM_HOST, +				      (7<<1) | (1<<4), 1, +				      "WRITE(6)"); +		if (reply == 0) +			reply = do_write(common); +		break; + +	case SC_WRITE_10: +		common->data_size_from_cmnd = +				get_unaligned_be16(&common->cmnd[7]) << 9; +		reply = check_command(common, 10, DATA_DIR_FROM_HOST, +				      (1<<1) | (0xf<<2) | (3<<7), 1, +				      "WRITE(10)"); +		if (reply == 0) +			reply = do_write(common); +		break; + +	case SC_WRITE_12: +		common->data_size_from_cmnd = +				get_unaligned_be32(&common->cmnd[6]) << 9; +		reply = check_command(common, 12, DATA_DIR_FROM_HOST, +				      (1<<1) | (0xf<<2) | (0xf<<6), 1, +				      "WRITE(12)"); +		if (reply == 0) +			reply = do_write(common); +		break; + +	/* Some mandatory commands that we recognize but don't implement. +	 * They don't mean much in this setting.  It's left as an exercise +	 * for anyone interested to implement RESERVE and RELEASE in terms +	 * of Posix locks. */ +	case SC_FORMAT_UNIT: +	case SC_RELEASE: +	case SC_RESERVE: +	case SC_SEND_DIAGNOSTIC: +		/* Fall through */ + +	default: +unknown_cmnd: +		common->data_size_from_cmnd = 0; +		sprintf(unknown, "Unknown x%02x", common->cmnd[0]); +		reply = check_command(common, common->cmnd_size, +				      DATA_DIR_UNKNOWN, 0xff, 0, unknown); +		if (reply == 0) { +			curlun->sense_data = SS_INVALID_COMMAND; +			reply = -EINVAL; +		} +		break; +	} +	up_read(&common->filesem); + +	if (reply == -EINTR) +		return -EINTR; + +	/* Set up the single reply buffer for finish_reply() */ +	if (reply == -EINVAL) +		reply = 0;		/* Error reply length */ +	if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) { +		reply = min((u32) reply, common->data_size_from_cmnd); +		bh->inreq->length = reply; +		bh->state = BUF_STATE_FULL; +		common->residue -= reply; +	}				/* Otherwise it's already set */ + +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ +	struct usb_request	*req = bh->outreq; +	struct fsg_bulk_cb_wrap	*cbw = req->buf; +	struct fsg_common	*common = fsg->common; + +	/* Was this a real packet?  Should it be ignored? */ +	if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) +		return -EINVAL; + +	/* Is the CBW valid? */ +	if (req->actual != USB_BULK_CB_WRAP_LEN || +			cbw->Signature != cpu_to_le32( +				USB_BULK_CB_SIG)) { +		DBG(fsg, "invalid CBW: len %u sig 0x%x\n", +				req->actual, +				le32_to_cpu(cbw->Signature)); + +		/* The Bulk-only spec says we MUST stall the IN endpoint +		 * (6.6.1), so it's unavoidable.  It also says we must +		 * retain this state until the next reset, but there's +		 * no way to tell the controller driver it should ignore +		 * Clear-Feature(HALT) requests. +		 * +		 * We aren't required to halt the OUT endpoint; instead +		 * we can simply accept and discard any data received +		 * until the next reset. */ +		wedge_bulk_in_endpoint(fsg); +		set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); +		return -EINVAL; +	} + +	/* Is the CBW meaningful? */ +	if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG || +			cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { +		DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " +				"cmdlen %u\n", +				cbw->Lun, cbw->Flags, cbw->Length); + +		/* We can do anything we want here, so let's stall the +		 * bulk pipes if we are allowed to. */ +		if (common->can_stall) { +			fsg_set_halt(fsg, fsg->bulk_out); +			halt_bulk_in_endpoint(fsg); +		} +		return -EINVAL; +	} + +	/* Save the command for later */ +	common->cmnd_size = cbw->Length; +	memcpy(common->cmnd, cbw->CDB, common->cmnd_size); +	if (cbw->Flags & USB_BULK_IN_FLAG) +		common->data_dir = DATA_DIR_TO_HOST; +	else +		common->data_dir = DATA_DIR_FROM_HOST; +	common->data_size = le32_to_cpu(cbw->DataTransferLength); +	if (common->data_size == 0) +		common->data_dir = DATA_DIR_NONE; +	common->lun = cbw->Lun; +	common->tag = cbw->Tag; +	return 0; +} + + +static int get_next_command(struct fsg_common *common) +{ +	struct fsg_buffhd	*bh; +	int			rc = 0; + +	/* Wait for the next buffer to become available */ +	bh = common->next_buffhd_to_fill; +	while (bh->state != BUF_STATE_EMPTY) { +		rc = sleep_thread(common); +		if (rc) +			return rc; +	} + +	/* Queue a request to read a Bulk-only CBW */ +	set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN); +	bh->outreq->short_not_ok = 1; +	START_TRANSFER_OR(common, bulk_out, bh->outreq, +			  &bh->outreq_busy, &bh->state) +		/* Don't know what to do if common->fsg is NULL */ +		return -EIO; + +	/* We will drain the buffer in software, which means we +	 * can reuse it for the next filling.  No need to advance +	 * next_buffhd_to_fill. */ + +	/* Wait for the CBW to arrive */ +	while (bh->state != BUF_STATE_FULL) { +		rc = sleep_thread(common); +		if (rc) +			return rc; +	} + +	rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO; +	bh->state = BUF_STATE_EMPTY; + +	return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int enable_endpoint(struct fsg_common *common, struct usb_ep *ep, +		const struct usb_endpoint_descriptor *d) +{ +	int	rc; + +	ep->driver_data = common; +	rc = usb_ep_enable(ep, d); +	if (rc) +		ERROR(common, "can't enable %s, result %d\n", ep->name, rc); +	return rc; +} + +static int alloc_request(struct fsg_common *common, struct usb_ep *ep, +		struct usb_request **preq) +{ +	*preq = usb_ep_alloc_request(ep, GFP_ATOMIC); +	if (*preq) +		return 0; +	ERROR(common, "can't allocate request for %s\n", ep->name); +	return -ENOMEM; +} + +/* Reset interface setting and re-init endpoint state (toggle etc). */ +static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg) +{ +	const struct usb_endpoint_descriptor *d; +	struct fsg_dev *fsg; +	int i, rc = 0; + +	if (common->running) +		DBG(common, "reset interface\n"); + +reset: +	/* Deallocate the requests */ +	if (common->fsg) { +		fsg = common->fsg; + +		for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +			struct fsg_buffhd *bh = &common->buffhds[i]; + +			if (bh->inreq) { +				usb_ep_free_request(fsg->bulk_in, bh->inreq); +				bh->inreq = NULL; +			} +			if (bh->outreq) { +				usb_ep_free_request(fsg->bulk_out, bh->outreq); +				bh->outreq = NULL; +			} +		} + +		/* Disable the endpoints */ +		if (fsg->bulk_in_enabled) { +			usb_ep_disable(fsg->bulk_in); +			fsg->bulk_in_enabled = 0; +		} +		if (fsg->bulk_out_enabled) { +			usb_ep_disable(fsg->bulk_out); +			fsg->bulk_out_enabled = 0; +		} + +		common->fsg = NULL; +		/* wake_up(&common->fsg_wait); */ +	} + +	common->running = 0; +	if (!new_fsg || rc) +		return rc; + +	common->fsg = new_fsg; +	fsg = common->fsg; + +	/* Enable the endpoints */ +	d = fsg_ep_desc(common->gadget, +			&fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); +	rc = enable_endpoint(common, fsg->bulk_in, d); +	if (rc) +		goto reset; +	fsg->bulk_in_enabled = 1; + +	d = fsg_ep_desc(common->gadget, +			&fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); +	rc = enable_endpoint(common, fsg->bulk_out, d); +	if (rc) +		goto reset; +	fsg->bulk_out_enabled = 1; +	common->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); +	clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + +	/* Allocate the requests */ +	for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +		struct fsg_buffhd	*bh = &common->buffhds[i]; + +		rc = alloc_request(common, fsg->bulk_in, &bh->inreq); +		if (rc) +			goto reset; +		rc = alloc_request(common, fsg->bulk_out, &bh->outreq); +		if (rc) +			goto reset; +		bh->inreq->buf = bh->outreq->buf = bh->buf; +		bh->inreq->context = bh->outreq->context = bh; +		bh->inreq->complete = bulk_in_complete; +		bh->outreq->complete = bulk_out_complete; +	} + +	common->running = 1; + +	return rc; +} + + +/****************************** ALT CONFIGS ******************************/ + + +static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ +	struct fsg_dev *fsg = fsg_from_func(f); +	fsg->common->new_fsg = fsg; +	raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); +	return 0; +} + +static void fsg_disable(struct usb_function *f) +{ +	struct fsg_dev *fsg = fsg_from_func(f); +	fsg->common->new_fsg = NULL; +	raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); +} + +/*-------------------------------------------------------------------------*/ + +static void handle_exception(struct fsg_common *common) +{ +	int			i; +	struct fsg_buffhd	*bh; +	enum fsg_state		old_state; +	struct fsg_lun		*curlun; +	unsigned int		exception_req_tag; + +	/* Cancel all the pending transfers */ +	if (common->fsg) { +		for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +			bh = &common->buffhds[i]; +			if (bh->inreq_busy) +				usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); +			if (bh->outreq_busy) +				usb_ep_dequeue(common->fsg->bulk_out, +					       bh->outreq); +		} + +		/* Wait until everything is idle */ +		for (;;) { +			int num_active = 0; +			for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +				bh = &common->buffhds[i]; +				num_active += bh->inreq_busy + bh->outreq_busy; +			} +			if (num_active == 0) +				break; +			if (sleep_thread(common)) +				return; +		} + +		/* Clear out the controller's fifos */ +		if (common->fsg->bulk_in_enabled) +			usb_ep_fifo_flush(common->fsg->bulk_in); +		if (common->fsg->bulk_out_enabled) +			usb_ep_fifo_flush(common->fsg->bulk_out); +	} + +	/* Reset the I/O buffer states and pointers, the SCSI +	 * state, and the exception.  Then invoke the handler. */ + +	for (i = 0; i < FSG_NUM_BUFFERS; ++i) { +		bh = &common->buffhds[i]; +		bh->state = BUF_STATE_EMPTY; +	} +	common->next_buffhd_to_fill = &common->buffhds[0]; +	common->next_buffhd_to_drain = &common->buffhds[0]; +	exception_req_tag = common->exception_req_tag; +	old_state = common->state; + +	if (old_state == FSG_STATE_ABORT_BULK_OUT) +		common->state = FSG_STATE_STATUS_PHASE; +	else { +		for (i = 0; i < common->nluns; ++i) { +			curlun = &common->luns[i]; +			curlun->sense_data = SS_NO_SENSE; +			curlun->info_valid = 0; +		} +		common->state = FSG_STATE_IDLE; +	} + +	/* Carry out any extra actions required for the exception */ +	switch (old_state) { +	case FSG_STATE_ABORT_BULK_OUT: +		send_status(common); + +		if (common->state == FSG_STATE_STATUS_PHASE) +			common->state = FSG_STATE_IDLE; +		break; + +	case FSG_STATE_RESET: +		/* In case we were forced against our will to halt a +		 * bulk endpoint, clear the halt now.  (The SuperH UDC +		 * requires this.) */ +		if (!fsg_is_set(common)) +			break; +		if (test_and_clear_bit(IGNORE_BULK_OUT, +				       &common->fsg->atomic_bitflags)) +			usb_ep_clear_halt(common->fsg->bulk_in); + +		if (common->ep0_req_tag == exception_req_tag) +			ep0_queue(common);	/* Complete the status stage */ + +		break; + +	case FSG_STATE_CONFIG_CHANGE: +		do_set_interface(common, common->new_fsg); +		break; + +	case FSG_STATE_EXIT: +	case FSG_STATE_TERMINATED: +		do_set_interface(common, NULL);		/* Free resources */ +		common->state = FSG_STATE_TERMINATED;	/* Stop the thread */ +		break; + +	case FSG_STATE_INTERFACE_CHANGE: +	case FSG_STATE_DISCONNECT: +	case FSG_STATE_COMMAND_PHASE: +	case FSG_STATE_DATA_PHASE: +	case FSG_STATE_STATUS_PHASE: +	case FSG_STATE_IDLE: +		break; +	} +} + +/*-------------------------------------------------------------------------*/ + +int fsg_main_thread(void *common_) +{ +	struct fsg_common	*common = the_fsg_common; +	/* The main loop */ +	do { +		if (exception_in_progress(common)) { +			handle_exception(common); +			continue; +		} + +		if (!common->running) { +			sleep_thread(common); +			continue; +		} + +		if (get_next_command(common)) +			continue; + +		if (!exception_in_progress(common)) +			common->state = FSG_STATE_DATA_PHASE; + +		if (do_scsi_command(common) || finish_reply(common)) +			continue; + +		if (!exception_in_progress(common)) +			common->state = FSG_STATE_STATUS_PHASE; + +		if (send_status(common)) +			continue; + +		if (!exception_in_progress(common)) +			common->state = FSG_STATE_IDLE; +	} while (0); + +	common->thread_task = NULL; + +	return 0; +} + +static void fsg_common_release(struct kref *ref); + +static struct fsg_common *fsg_common_init(struct fsg_common *common, +					  struct usb_composite_dev *cdev) +{ +	struct usb_gadget *gadget = cdev->gadget; +	struct fsg_buffhd *bh; +	struct fsg_lun *curlun; +	int nluns, i, rc; + +	/* Find out how many LUNs there should be */ +	nluns = 1; +	if (nluns < 1 || nluns > FSG_MAX_LUNS) { +		printf("invalid number of LUNs: %u\n", nluns); +		return ERR_PTR(-EINVAL); +	} + +	/* Allocate? */ +	if (!common) { +		common = calloc(sizeof *common, 1); +		if (!common) +			return ERR_PTR(-ENOMEM); +		common->free_storage_on_release = 1; +	} else { +		memset(common, 0, sizeof common); +		common->free_storage_on_release = 0; +	} + +	common->ops = NULL; +	common->private_data = NULL; + +	common->gadget = gadget; +	common->ep0 = gadget->ep0; +	common->ep0req = cdev->req; + +	/* Maybe allocate device-global string IDs, and patch descriptors */ +	if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { +		rc = usb_string_id(cdev); +		if (unlikely(rc < 0)) +			goto error_release; +		fsg_strings[FSG_STRING_INTERFACE].id = rc; +		fsg_intf_desc.iInterface = rc; +	} + +	/* Create the LUNs, open their backing files, and register the +	 * LUN devices in sysfs. */ +	curlun = calloc(nluns, sizeof *curlun); +	if (!curlun) { +		rc = -ENOMEM; +		goto error_release; +	} +	common->nluns = nluns; + +	for (i = 0; i < nluns; i++) { +		common->luns[i].removable = 1; + +		rc = fsg_lun_open(&common->luns[i], ""); +		if (rc) +			goto error_luns; +	} +	common->lun = 0; + +	/* Data buffers cyclic list */ +	bh = common->buffhds; + +	i = FSG_NUM_BUFFERS; +	goto buffhds_first_it; +	do { +		bh->next = bh + 1; +		++bh; +buffhds_first_it: +		bh->inreq_busy = 0; +		bh->outreq_busy = 0; +		bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL); +		if (unlikely(!bh->buf)) { +			rc = -ENOMEM; +			goto error_release; +		} +	} while (--i); +	bh->next = common->buffhds; + +	snprintf(common->inquiry_string, sizeof common->inquiry_string, +		 "%-8s%-16s%04x", +		 "Linux   ", +		 "File-Store Gadget", +		 0xffff); + +	/* Some peripheral controllers are known not to be able to +	 * halt bulk endpoints correctly.  If one of them is present, +	 * disable stalls. +	 */ + +	/* Tell the thread to start working */ +	common->thread_task = +		kthread_create(fsg_main_thread, common, +			       OR(cfg->thread_name, "file-storage")); +	if (IS_ERR(common->thread_task)) { +		rc = PTR_ERR(common->thread_task); +		goto error_release; +	} + +#undef OR +	/* Information */ +	INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); +	INFO(common, "Number of LUNs=%d\n", common->nluns); + +	return common; + +error_luns: +	common->nluns = i + 1; +error_release: +	common->state = FSG_STATE_TERMINATED;	/* The thread is dead */ +	/* Call fsg_common_release() directly, ref might be not +	 * initialised */ +	fsg_common_release(&common->ref); +	return ERR_PTR(rc); +} + +static void fsg_common_release(struct kref *ref) +{ +	struct fsg_common *common = container_of(ref, struct fsg_common, ref); + +	/* If the thread isn't already dead, tell it to exit now */ +	if (common->state != FSG_STATE_TERMINATED) { +		raise_exception(common, FSG_STATE_EXIT); +		wait_for_completion(&common->thread_notifier); +	} + +	if (likely(common->luns)) { +		struct fsg_lun *lun = common->luns; +		unsigned i = common->nluns; + +		/* In error recovery common->nluns may be zero. */ +		for (; i; --i, ++lun) +			fsg_lun_close(lun); + +		kfree(common->luns); +	} + +	{ +		struct fsg_buffhd *bh = common->buffhds; +		unsigned i = FSG_NUM_BUFFERS; +		do { +			kfree(bh->buf); +		} while (++bh, --i); +	} + +	if (common->free_storage_on_release) +		kfree(common); +} + + +/*-------------------------------------------------------------------------*/ + +/** + * usb_copy_descriptors - copy a vector of USB descriptors + * @src: null-terminated vector to copy + * Context: initialization code, which may sleep + * + * This makes a copy of a vector of USB descriptors.  Its primary use + * is to support usb_function objects which can have multiple copies, + * each needing different descriptors.  Functions may have static + * tables of descriptors, which are used as templates and customized + * with identifiers (for interfaces, strings, endpoints, and more) + * as needed by a given function instance. + */ +struct usb_descriptor_header ** +usb_copy_descriptors(struct usb_descriptor_header **src) +{ +	struct usb_descriptor_header **tmp; +	unsigned bytes; +	unsigned n_desc; +	void *mem; +	struct usb_descriptor_header **ret; + +	/* count descriptors and their sizes; then add vector size */ +	for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++) +		bytes += (*tmp)->bLength; +	bytes += (n_desc + 1) * sizeof(*tmp); + +	mem = kmalloc(bytes, GFP_KERNEL); +	if (!mem) +		return NULL; + +	/* fill in pointers starting at "tmp", +	 * to descriptors copied starting at "mem"; +	 * and return "ret" +	 */ +	tmp = mem; +	ret = mem; +	mem += (n_desc + 1) * sizeof(*tmp); +	while (*src) { +		memcpy(mem, *src, (*src)->bLength); +		*tmp = mem; +		tmp++; +		mem += (*src)->bLength; +		src++; +	} +	*tmp = NULL; + +	return ret; +} + + + +static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) +{ +	struct fsg_dev		*fsg = fsg_from_func(f); + +	DBG(fsg, "unbind\n"); +	if (fsg->common->fsg == fsg) { +		fsg->common->new_fsg = NULL; +		raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); +	} + +	free(fsg->function.descriptors); +	free(fsg->function.hs_descriptors); +	kfree(fsg); +} + +static int fsg_bind(struct usb_configuration *c, struct usb_function *f) +{ +	struct fsg_dev		*fsg = fsg_from_func(f); +	struct usb_gadget	*gadget = c->cdev->gadget; +	int			i; +	struct usb_ep		*ep; +	fsg->gadget = gadget; + +	/* New interface */ +	i = usb_interface_id(c, f); +	if (i < 0) +		return i; +	fsg_intf_desc.bInterfaceNumber = i; +	fsg->interface_number = i; + +	/* Find all the endpoints we will use */ +	ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); +	if (!ep) +		goto autoconf_fail; +	ep->driver_data = fsg->common;	/* claim the endpoint */ +	fsg->bulk_in = ep; + +	ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); +	if (!ep) +		goto autoconf_fail; +	ep->driver_data = fsg->common;	/* claim the endpoint */ +	fsg->bulk_out = ep; + +	/* Copy descriptors */ +	f->descriptors = usb_copy_descriptors(fsg_fs_function); +	if (unlikely(!f->descriptors)) +		return -ENOMEM; + +	if (gadget_is_dualspeed(gadget)) { +		/* Assume endpoint addresses are the same for both speeds */ +		fsg_hs_bulk_in_desc.bEndpointAddress = +			fsg_fs_bulk_in_desc.bEndpointAddress; +		fsg_hs_bulk_out_desc.bEndpointAddress = +			fsg_fs_bulk_out_desc.bEndpointAddress; +		f->hs_descriptors = usb_copy_descriptors(fsg_hs_function); +		if (unlikely(!f->hs_descriptors)) { +			free(f->descriptors); +			return -ENOMEM; +		} +	} +	return 0; + +autoconf_fail: +	ERROR(fsg, "unable to autoconfigure all endpoints\n"); +	return -ENOTSUPP; +} + + +/****************************** ADD FUNCTION ******************************/ + +static struct usb_gadget_strings *fsg_strings_array[] = { +	&fsg_stringtab, +	NULL, +}; + +static int fsg_bind_config(struct usb_composite_dev *cdev, +			   struct usb_configuration *c, +			   struct fsg_common *common) +{ +	struct fsg_dev *fsg; +	int rc; + +	fsg = calloc(1, sizeof *fsg); +	if (!fsg) +		return -ENOMEM; +	fsg->function.name        = FSG_DRIVER_DESC; +	fsg->function.strings     = fsg_strings_array; +	fsg->function.bind        = fsg_bind; +	fsg->function.unbind      = fsg_unbind; +	fsg->function.setup       = fsg_setup; +	fsg->function.set_alt     = fsg_set_alt; +	fsg->function.disable     = fsg_disable; + +	fsg->common               = common; +	common->fsg               = fsg; +	/* Our caller holds a reference to common structure so we +	 * don't have to be worry about it being freed until we return +	 * from this function.  So instead of incrementing counter now +	 * and decrement in error recovery we increment it only when +	 * call to usb_add_function() was successful. */ + +	rc = usb_add_function(c, &fsg->function); + +	if (rc) +		kfree(fsg); + +	return rc; +} + +int fsg_add(struct usb_configuration *c) +{ +	struct fsg_common *fsg_common; + +	fsg_common = fsg_common_init(NULL, c->cdev); + +	fsg_common->vendor_name = 0; +	fsg_common->product_name = 0; +	fsg_common->release = 0xffff; + +	fsg_common->ops = NULL; +	fsg_common->private_data = NULL; + +	the_fsg_common = fsg_common; + +	return fsg_bind_config(c->cdev, c, fsg_common); +} + +int fsg_init(struct ums_board_info *ums) +{ +	ums_info = ums; + +	return 0; +} diff --git a/drivers/usb/gadget/g_dnl.c b/drivers/usb/gadget/g_dnl.c index a5a4c1fe6..cc3f3449c 100644 --- a/drivers/usb/gadget/g_dnl.c +++ b/drivers/usb/gadget/g_dnl.c @@ -31,6 +31,7 @@  #include "gadget_chips.h"  #include "composite.c" +#include "f_mass_storage.c"  /*   * One needs to define the following: @@ -104,6 +105,8 @@ static int g_dnl_do_config(struct usb_configuration *c)  	printf("GADGET DRIVER: %s\n", s);  	if (!strcmp(s, "usb_dnl_dfu"))  		ret = dfu_add(c); +	else if (!strcmp(s, "usb_dnl_ums")) +		ret = fsg_add(c);  	return ret;  } @@ -188,6 +191,9 @@ int g_dnl_register(const char *type)  	if (!strcmp(type, "dfu")) {  		strcpy(name, shortname);  		strcat(name, type); +	} else if (!strcmp(type, "ums")) { +		strcpy(name, shortname); +		strcat(name, type);  	} else {  		printf("%s: unknown command: %s\n", __func__, type);  		return -EINVAL; diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c new file mode 100644 index 000000000..594dc10af --- /dev/null +++ b/drivers/usb/gadget/storage_common.c @@ -0,0 +1,653 @@ +/* + * storage_common.c -- Common definitions for mass storage functionality + * + * Copyright (C) 2003-2008 Alan Stern + * Copyeight (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) + * + * Ported to u-boot: + * Andrzej Pietrasiewicz <andrzej.p@samsung.com> + * + * Code refactoring & cleanup: + * Łukasz Majewski <l.majewski@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + */ + + +/* + * This file requires the following identifiers used in USB strings to + * be defined (each of type pointer to char): + *  - fsg_string_manufacturer -- name of the manufacturer + *  - fsg_string_product      -- name of the product + *  - fsg_string_serial       -- product's serial + *  - fsg_string_config       -- name of the configuration + *  - fsg_string_interface    -- name of the interface + * The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS + * macro is defined prior to including this file. + */ + +/* + * When FSG_NO_INTR_EP is defined fsg_fs_intr_in_desc and + * fsg_hs_intr_in_desc objects as well as + * FSG_FS_FUNCTION_PRE_EP_ENTRIES and FSG_HS_FUNCTION_PRE_EP_ENTRIES + * macros are not defined. + * + * When FSG_NO_DEVICE_STRINGS is defined FSG_STRING_MANUFACTURER, + * FSG_STRING_PRODUCT, FSG_STRING_SERIAL and FSG_STRING_CONFIG are not + * defined (as well as corresponding entries in string tables are + * missing) and FSG_STRING_INTERFACE has value of zero. + * + * When FSG_NO_OTG is defined fsg_otg_desc won't be defined. + */ + +/* + * When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included + * the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN + * characters rather then a pointer to void. + */ + + +/* #include <asm/unaligned.h> */ + + +/* + * Thanks to NetChip Technologies for donating this product ID. + * + * DO NOT REUSE THESE IDs with any other driver!!  Ever!! + * Instead:  allocate your own, using normal USB-IF procedures. + */ +#define FSG_VENDOR_ID	0x0525	/* NetChip */ +#define FSG_PRODUCT_ID	0xa4a5	/* Linux-USB File-backed Storage Gadget */ + +/*-------------------------------------------------------------------------*/ + +#ifndef DEBUG +#undef VERBOSE_DEBUG +#undef DUMP_MSGS +#endif /* !DEBUG */ + +#ifdef VERBOSE_DEBUG +#define VLDBG	LDBG +#else +#define VLDBG(lun, fmt, args...) do { } while (0) +#endif /* VERBOSE_DEBUG */ + +/* +#define LDBG(lun, fmt, args...)   dev_dbg (&(lun)->dev, fmt, ## args) +#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args) +#define LWARN(lun, fmt, args...)  dev_warn(&(lun)->dev, fmt, ## args) +#define LINFO(lun, fmt, args...)  dev_info(&(lun)->dev, fmt, ## args) +*/ + +#define LDBG(lun, fmt, args...) do { } while (0) +#define LERROR(lun, fmt, args...) do { } while (0) +#define LWARN(lun, fmt, args...) do { } while (0) +#define LINFO(lun, fmt, args...) do { } while (0) + +/* + * Keep those macros in sync with those in + * include/linux/usb/composite.h or else GCC will complain.  If they + * are identical (the same names of arguments, white spaces in the + * same places) GCC will allow redefinition otherwise (even if some + * white space is removed or added) warning will be issued. + * + * Those macros are needed here because File Storage Gadget does not + * include the composite.h header.  For composite gadgets those macros + * are redundant since composite.h is included any way. + * + * One could check whether those macros are already defined (which + * would indicate composite.h had been included) or not (which would + * indicate we were in FSG) but this is not done because a warning is + * desired if definitions here differ from the ones in composite.h. + * + * We want the definitions to match and be the same in File Storage + * Gadget as well as Mass Storage Function (and so composite gadgets + * using MSF).  If someone changes them in composite.h it will produce + * a warning in this file when building MSF. + */ + +#define DBG(d, fmt, args...)     debug(fmt , ## args) +#define VDBG(d, fmt, args...)    debug(fmt , ## args) +/* #define ERROR(d, fmt, args...)   printf(fmt , ## args) */ +/* #define WARNING(d, fmt, args...) printf(fmt , ## args) */ +/* #define INFO(d, fmt, args...)    printf(fmt , ## args) */ + +/* #define DBG(d, fmt, args...)     do { } while (0) */ +/* #define VDBG(d, fmt, args...)    do { } while (0) */ +#define ERROR(d, fmt, args...)   do { } while (0) +#define WARNING(d, fmt, args...) do { } while (0) +#define INFO(d, fmt, args...)    do { } while (0) + +#ifdef DUMP_MSGS + +/* dump_msg(fsg, const char * label, const u8 * buf, unsigned length); */ +# define dump_msg(fsg, label, buf, length) do {                         \ +	if (length < 512) {						\ +		DBG(fsg, "%s, length %u:\n", label, length);		\ +		print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET,	\ +			       16, 1, buf, length, 0);			\ +	}								\ +} while (0) + +#  define dump_cdb(fsg) do { } while (0) + +#else + +#  define dump_msg(fsg, /* const char * */ label, \ +		   /* const u8 * */ buf, /* unsigned */ length) do { } while (0) + +#  ifdef VERBOSE_DEBUG + +#    define dump_cdb(fsg)						\ +	print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE,	\ +		       16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0)		\ + +#  else + +#    define dump_cdb(fsg) do { } while (0) + +#  endif /* VERBOSE_DEBUG */ + +#endif /* DUMP_MSGS */ + +/*-------------------------------------------------------------------------*/ + +/* SCSI device types */ +#define TYPE_DISK	0x00 +#define TYPE_CDROM	0x05 + +/* USB protocol value = the transport method */ +#define USB_PR_CBI	0x00		/* Control/Bulk/Interrupt */ +#define USB_PR_CB	0x01		/* Control/Bulk w/o interrupt */ +#define USB_PR_BULK	0x50		/* Bulk-only */ + +/* USB subclass value = the protocol encapsulation */ +#define USB_SC_RBC	0x01		/* Reduced Block Commands (flash) */ +#define USB_SC_8020	0x02		/* SFF-8020i, MMC-2, ATAPI (CD-ROM) */ +#define USB_SC_QIC	0x03		/* QIC-157 (tape) */ +#define USB_SC_UFI	0x04		/* UFI (floppy) */ +#define USB_SC_8070	0x05		/* SFF-8070i (removable) */ +#define USB_SC_SCSI	0x06		/* Transparent SCSI */ + +/* Bulk-only data structures */ + +/* Command Block Wrapper */ +struct fsg_bulk_cb_wrap { +	__le32	Signature;		/* Contains 'USBC' */ +	u32	Tag;			/* Unique per command id */ +	__le32	DataTransferLength;	/* Size of the data */ +	u8	Flags;			/* Direction in bit 7 */ +	u8	Lun;			/* LUN (normally 0) */ +	u8	Length;			/* Of the CDB, <= MAX_COMMAND_SIZE */ +	u8	CDB[16];		/* Command Data Block */ +}; + +#define USB_BULK_CB_WRAP_LEN	31 +#define USB_BULK_CB_SIG		0x43425355	/* Spells out USBC */ +#define USB_BULK_IN_FLAG	0x80 + +/* Command Status Wrapper */ +struct bulk_cs_wrap { +	__le32	Signature;		/* Should = 'USBS' */ +	u32	Tag;			/* Same as original command */ +	__le32	Residue;		/* Amount not transferred */ +	u8	Status;			/* See below */ +}; + +#define USB_BULK_CS_WRAP_LEN	13 +#define USB_BULK_CS_SIG		0x53425355	/* Spells out 'USBS' */ +#define USB_STATUS_PASS		0 +#define USB_STATUS_FAIL		1 +#define USB_STATUS_PHASE_ERROR	2 + +/* Bulk-only class specific requests */ +#define USB_BULK_RESET_REQUEST		0xff +#define USB_BULK_GET_MAX_LUN_REQUEST	0xfe + +/* CBI Interrupt data structure */ +struct interrupt_data { +	u8	bType; +	u8	bValue; +}; + +#define CBI_INTERRUPT_DATA_LEN		2 + +/* CBI Accept Device-Specific Command request */ +#define USB_CBI_ADSC_REQUEST		0x00 + +/* Length of a SCSI Command Data Block */ +#define MAX_COMMAND_SIZE	16 + +/* SCSI commands that we recognize */ +#define SC_FORMAT_UNIT			0x04 +#define SC_INQUIRY			0x12 +#define SC_MODE_SELECT_6		0x15 +#define SC_MODE_SELECT_10		0x55 +#define SC_MODE_SENSE_6			0x1a +#define SC_MODE_SENSE_10		0x5a +#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL	0x1e +#define SC_READ_6			0x08 +#define SC_READ_10			0x28 +#define SC_READ_12			0xa8 +#define SC_READ_CAPACITY		0x25 +#define SC_READ_FORMAT_CAPACITIES	0x23 +#define SC_READ_HEADER			0x44 +#define SC_READ_TOC			0x43 +#define SC_RELEASE			0x17 +#define SC_REQUEST_SENSE		0x03 +#define SC_RESERVE			0x16 +#define SC_SEND_DIAGNOSTIC		0x1d +#define SC_START_STOP_UNIT		0x1b +#define SC_SYNCHRONIZE_CACHE		0x35 +#define SC_TEST_UNIT_READY		0x00 +#define SC_VERIFY			0x2f +#define SC_WRITE_6			0x0a +#define SC_WRITE_10			0x2a +#define SC_WRITE_12			0xaa + +/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ +#define SS_NO_SENSE				0 +#define SS_COMMUNICATION_FAILURE		0x040800 +#define SS_INVALID_COMMAND			0x052000 +#define SS_INVALID_FIELD_IN_CDB			0x052400 +#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE	0x052100 +#define SS_LOGICAL_UNIT_NOT_SUPPORTED		0x052500 +#define SS_MEDIUM_NOT_PRESENT			0x023a00 +#define SS_MEDIUM_REMOVAL_PREVENTED		0x055302 +#define SS_NOT_READY_TO_READY_TRANSITION	0x062800 +#define SS_RESET_OCCURRED			0x062900 +#define SS_SAVING_PARAMETERS_NOT_SUPPORTED	0x053900 +#define SS_UNRECOVERED_READ_ERROR		0x031100 +#define SS_WRITE_ERROR				0x030c02 +#define SS_WRITE_PROTECTED			0x072700 + +#define SK(x)		((u8) ((x) >> 16))	/* Sense Key byte, etc. */ +#define ASC(x)		((u8) ((x) >> 8)) +#define ASCQ(x)		((u8) (x)) + +struct device_attribute { int i; }; +struct rw_semaphore { int i; }; +#define down_write(...)			do { } while (0) +#define up_write(...)			do { } while (0) +#define down_read(...)			do { } while (0) +#define up_read(...)			do { } while (0) +#define ETOOSMALL	525 + +#include <usb_mass_storage.h> +extern struct ums_board_info		*ums_info; + +/*-------------------------------------------------------------------------*/ + +struct fsg_lun { +	loff_t		file_length; +	loff_t		num_sectors; + +	unsigned int	initially_ro:1; +	unsigned int	ro:1; +	unsigned int	removable:1; +	unsigned int	cdrom:1; +	unsigned int	prevent_medium_removal:1; +	unsigned int	registered:1; +	unsigned int	info_valid:1; +	unsigned int	nofua:1; + +	u32		sense_data; +	u32		sense_data_info; +	u32		unit_attention_data; + +	struct device	dev; +}; + +#define fsg_lun_is_open(curlun)	((curlun)->filp != NULL) +#if 0 +static struct fsg_lun *fsg_lun_from_dev(struct device *dev) +{ +	return container_of(dev, struct fsg_lun, dev); +} +#endif + +/* Big enough to hold our biggest descriptor */ +#define EP0_BUFSIZE	256 +#define DELAYED_STATUS	(EP0_BUFSIZE + 999)	/* An impossibly large value */ + +/* Number of buffers we will use.  2 is enough for double-buffering */ +#define FSG_NUM_BUFFERS	2 + +/* Default size of buffer length. */ +#define FSG_BUFLEN	((u32)16384) + +/* Maximal number of LUNs supported in mass storage function */ +#define FSG_MAX_LUNS	8 + +enum fsg_buffer_state { +	BUF_STATE_EMPTY = 0, +	BUF_STATE_FULL, +	BUF_STATE_BUSY +}; + +struct fsg_buffhd { +#ifdef FSG_BUFFHD_STATIC_BUFFER +	char				buf[FSG_BUFLEN]; +#else +	void				*buf; +#endif +	enum fsg_buffer_state		state; +	struct fsg_buffhd		*next; + +	/* +	 * The NetChip 2280 is faster, and handles some protocol faults +	 * better, if we don't submit any short bulk-out read requests. +	 * So we will record the intended request length here. +	 */ +	unsigned int			bulk_out_intended_length; + +	struct usb_request		*inreq; +	int				inreq_busy; +	struct usb_request		*outreq; +	int				outreq_busy; +}; + +enum fsg_state { +	/* This one isn't used anywhere */ +	FSG_STATE_COMMAND_PHASE = -10, +	FSG_STATE_DATA_PHASE, +	FSG_STATE_STATUS_PHASE, + +	FSG_STATE_IDLE = 0, +	FSG_STATE_ABORT_BULK_OUT, +	FSG_STATE_RESET, +	FSG_STATE_INTERFACE_CHANGE, +	FSG_STATE_CONFIG_CHANGE, +	FSG_STATE_DISCONNECT, +	FSG_STATE_EXIT, +	FSG_STATE_TERMINATED +}; + +enum data_direction { +	DATA_DIR_UNKNOWN = 0, +	DATA_DIR_FROM_HOST, +	DATA_DIR_TO_HOST, +	DATA_DIR_NONE +}; + +/*-------------------------------------------------------------------------*/ + +static inline u32 get_unaligned_be24(u8 *buf) +{ +	return 0xffffff & (u32) get_unaligned_be32(buf - 1); +} + +/*-------------------------------------------------------------------------*/ + +enum { +#ifndef FSG_NO_DEVICE_STRINGS +	FSG_STRING_MANUFACTURER	= 1, +	FSG_STRING_PRODUCT, +	FSG_STRING_SERIAL, +	FSG_STRING_CONFIG, +#endif +	FSG_STRING_INTERFACE +}; + +#ifndef FSG_NO_OTG +static struct usb_otg_descriptor +fsg_otg_desc = { +	.bLength =		sizeof fsg_otg_desc, +	.bDescriptorType =	USB_DT_OTG, + +	.bmAttributes =		USB_OTG_SRP, +}; +#endif + +/* There is only one interface. */ + +static struct usb_interface_descriptor +fsg_intf_desc = { +	.bLength =		sizeof fsg_intf_desc, +	.bDescriptorType =	USB_DT_INTERFACE, + +	.bNumEndpoints =	2,		/* Adjusted during fsg_bind() */ +	.bInterfaceClass =	USB_CLASS_MASS_STORAGE, +	.bInterfaceSubClass =	USB_SC_SCSI,	/* Adjusted during fsg_bind() */ +	.bInterfaceProtocol =	USB_PR_BULK,	/* Adjusted during fsg_bind() */ +	.iInterface =		FSG_STRING_INTERFACE, +}; + +/* + * Three full-speed endpoint descriptors: bulk-in, bulk-out, and + * interrupt-in. + */ + +static struct usb_endpoint_descriptor +fsg_fs_bulk_in_desc = { +	.bLength =		USB_DT_ENDPOINT_SIZE, +	.bDescriptorType =	USB_DT_ENDPOINT, + +	.bEndpointAddress =	USB_DIR_IN, +	.bmAttributes =		USB_ENDPOINT_XFER_BULK, +	/* wMaxPacketSize set by autoconfiguration */ +}; + +static struct usb_endpoint_descriptor +fsg_fs_bulk_out_desc = { +	.bLength =		USB_DT_ENDPOINT_SIZE, +	.bDescriptorType =	USB_DT_ENDPOINT, + +	.bEndpointAddress =	USB_DIR_OUT, +	.bmAttributes =		USB_ENDPOINT_XFER_BULK, +	/* wMaxPacketSize set by autoconfiguration */ +}; + +#ifndef FSG_NO_INTR_EP + +static struct usb_endpoint_descriptor +fsg_fs_intr_in_desc = { +	.bLength =		USB_DT_ENDPOINT_SIZE, +	.bDescriptorType =	USB_DT_ENDPOINT, + +	.bEndpointAddress =	USB_DIR_IN, +	.bmAttributes =		USB_ENDPOINT_XFER_INT, +	.wMaxPacketSize =	cpu_to_le16(2), +	.bInterval =		32,	/* frames -> 32 ms */ +}; + +#ifndef FSG_NO_OTG +#  define FSG_FS_FUNCTION_PRE_EP_ENTRIES	2 +#else +#  define FSG_FS_FUNCTION_PRE_EP_ENTRIES	1 +#endif + +#endif + +static struct usb_descriptor_header *fsg_fs_function[] = { +#ifndef FSG_NO_OTG +	(struct usb_descriptor_header *) &fsg_otg_desc, +#endif +	(struct usb_descriptor_header *) &fsg_intf_desc, +	(struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, +	(struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, +#ifndef FSG_NO_INTR_EP +	(struct usb_descriptor_header *) &fsg_fs_intr_in_desc, +#endif +	NULL, +}; + +/* + * USB 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + * + * That means alternate endpoint descriptors (bigger packets) + * and a "device qualifier" ... plus more construction options + * for the configuration descriptor. + */ +static struct usb_endpoint_descriptor +fsg_hs_bulk_in_desc = { +	.bLength =		USB_DT_ENDPOINT_SIZE, +	.bDescriptorType =	USB_DT_ENDPOINT, + +	/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ +	.bmAttributes =		USB_ENDPOINT_XFER_BULK, +	.wMaxPacketSize =	cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor +fsg_hs_bulk_out_desc = { +	.bLength =		USB_DT_ENDPOINT_SIZE, +	.bDescriptorType =	USB_DT_ENDPOINT, + +	/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ +	.bmAttributes =		USB_ENDPOINT_XFER_BULK, +	.wMaxPacketSize =	cpu_to_le16(512), +	.bInterval =		1,	/* NAK every 1 uframe */ +}; + +#ifndef FSG_NO_INTR_EP + +static struct usb_endpoint_descriptor +fsg_hs_intr_in_desc = { +	.bLength =		USB_DT_ENDPOINT_SIZE, +	.bDescriptorType =	USB_DT_ENDPOINT, + +	/* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ +	.bmAttributes =		USB_ENDPOINT_XFER_INT, +	.wMaxPacketSize =	cpu_to_le16(2), +	.bInterval =		9,	/* 2**(9-1) = 256 uframes -> 32 ms */ +}; + +#ifndef FSG_NO_OTG +#  define FSG_HS_FUNCTION_PRE_EP_ENTRIES	2 +#else +#  define FSG_HS_FUNCTION_PRE_EP_ENTRIES	1 +#endif + +#endif + +static struct usb_descriptor_header *fsg_hs_function[] = { +#ifndef FSG_NO_OTG +	(struct usb_descriptor_header *) &fsg_otg_desc, +#endif +	(struct usb_descriptor_header *) &fsg_intf_desc, +	(struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, +	(struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, +#ifndef FSG_NO_INTR_EP +	(struct usb_descriptor_header *) &fsg_hs_intr_in_desc, +#endif +	NULL, +}; + +/* Maxpacket and other transfer characteristics vary by speed. */ +static struct usb_endpoint_descriptor * +fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, +		struct usb_endpoint_descriptor *hs) +{ +	if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) +		return hs; +	return fs; +} + +/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ +static struct usb_string		fsg_strings[] = { +#ifndef FSG_NO_DEVICE_STRINGS +	{FSG_STRING_MANUFACTURER,	fsg_string_manufacturer}, +	{FSG_STRING_PRODUCT,		fsg_string_product}, +	{FSG_STRING_SERIAL,		fsg_string_serial}, +	{FSG_STRING_CONFIG,		fsg_string_config}, +#endif +	{FSG_STRING_INTERFACE,		fsg_string_interface}, +	{} +}; + +static struct usb_gadget_strings	fsg_stringtab = { +	.language	= 0x0409,		/* en-us */ +	.strings	= fsg_strings, +}; + +/*-------------------------------------------------------------------------*/ + +/* + * If the next two routines are called while the gadget is registered, + * the caller must own fsg->filesem for writing. + */ + +static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) +{ +	int				ro; +	int				rc = -EINVAL; +	loff_t				size; +	loff_t				num_sectors; +	loff_t				min_sectors; + +	/* R/W if we can, R/O if we must */ +	ro = curlun->initially_ro; + +	ums_info->get_capacity(&(ums_info->ums_dev), &size); +	if (size < 0) { +		printf("unable to find file size: %s\n", filename); +		rc = (int) size; +		goto out; +	} +	num_sectors = size >> 9;	/* File size in 512-byte blocks */ +	min_sectors = 1; +	if (num_sectors < min_sectors) { +		printf("file too small: %s\n", filename); +		rc = -ETOOSMALL; +		goto out; +	} + +	curlun->ro = ro; +	curlun->file_length = size; +	curlun->num_sectors = num_sectors; +	debug("open backing file: %s\n", filename); +	rc = 0; + +out: +	return rc; +} + +static void fsg_lun_close(struct fsg_lun *curlun) +{ +} + +/*-------------------------------------------------------------------------*/ + +/* + * Sync the file data, don't bother with the metadata. + * This code was copied from fs/buffer.c:sys_fdatasync(). + */ +static int fsg_lun_fsync_sub(struct fsg_lun *curlun) +{ +	return 0; +} + +static void store_cdrom_address(u8 *dest, int msf, u32 addr) +{ +	if (msf) { +		/* Convert to Minutes-Seconds-Frames */ +		addr >>= 2;		/* Convert to 2048-byte frames */ +		addr += 2*75;		/* Lead-in occupies 2 seconds */ +		dest[3] = addr % 75;	/* Frames */ +		addr /= 75; +		dest[2] = addr % 60;	/* Seconds */ +		addr /= 60; +		dest[1] = addr;		/* Minutes */ +		dest[0] = 0;		/* Reserved */ +	} else { +		/* Absolute sector */ +		put_unaligned_be32(addr, dest); +	} +} + +/*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 6c9479492..9a6f98208 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -54,6 +54,7 @@ COBJS-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o  COBJS-$(CONFIG_USB_EHCI_IXP4XX) += ehci-ixp.o  COBJS-$(CONFIG_USB_EHCI_MARVELL) += ehci-marvell.o  COBJS-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o +COBJS-$(CONFIG_USB_EHCI_SPEAR) += ehci-spear.o  COBJS-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o  COBJS-$(CONFIG_USB_EHCI_VCT) += ehci-vct.o diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index 3ca4c5c33..0c797aa04 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -42,11 +42,15 @@ DECLARE_GLOBAL_DATA_PTR;   */  struct exynos_ehci {  	struct exynos_usb_phy *usb; -	unsigned int *hcd; +	struct ehci_hccr *hcd;  }; +static struct exynos_ehci exynos; + +#ifdef CONFIG_OF_CONTROL  static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos)  { +	fdt_addr_t addr;  	unsigned int node;  	int depth; @@ -59,12 +63,14 @@ static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos)  	/*  	 * Get the base address for EHCI controller from the device node  	 */ -	exynos->hcd = (unsigned int *)fdtdec_get_addr(blob, node, "reg"); -	if (exynos->hcd == NULL) { +	addr = fdtdec_get_addr(blob, node, "reg"); +	if (addr == FDT_ADDR_T_NONE) {  		debug("Can't get the EHCI register address\n");  		return -ENXIO;  	} +	exynos->hcd = (struct ehci_hccr *)addr; +  	depth = 0;  	node = fdtdec_next_compatible_subnode(blob, node,  					COMPAT_SAMSUNG_EXYNOS_USB_PHY, &depth); @@ -85,6 +91,7 @@ static int exynos_usb_parse_dt(const void *blob, struct exynos_ehci *exynos)  	return 0;  } +#endif  /* Setup the EHCI host controller. */  static void setup_usb_phy(struct exynos_usb_phy *usb) @@ -144,20 +151,21 @@ static void reset_usb_phy(struct exynos_usb_phy *usb)   */  int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)  { -	struct exynos_ehci *exynos = NULL; +	struct exynos_ehci *ctx = &exynos; -	exynos = (struct exynos_ehci *) -			kzalloc(sizeof(struct exynos_ehci), GFP_KERNEL); -	if (!exynos) { -		debug("failed to allocate exynos ehci context\n"); -		return -ENOMEM; +#ifdef CONFIG_OF_CONTROL +	if (exynos_usb_parse_dt(gd->fdt_blob, ctx)) { +		debug("Unable to parse device tree for ehci-exynos\n"); +		return -ENODEV;  	} +#else +	ctx->usb = (struct exynos_usb_phy *)samsung_get_base_usb_phy(); +	ctx->hcd = (struct ehci_hccr *)samsung_get_base_usb_ehci(); +#endif -	exynos_usb_parse_dt(gd->fdt_blob, exynos); +	setup_usb_phy(ctx->usb); -	setup_usb_phy(exynos->usb); - -	*hccr = (struct ehci_hccr *)(exynos->hcd); +	*hccr = ctx->hcd;  	*hcor = (struct ehci_hcor *)((uint32_t) *hccr  				+ HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); @@ -165,8 +173,6 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)  		(uint32_t)*hccr, (uint32_t)*hcor,  		(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); -	kfree(exynos); -  	return 0;  } @@ -176,20 +182,9 @@ int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)   */  int ehci_hcd_stop(int index)  { -	struct exynos_ehci *exynos = NULL; - -	exynos = (struct exynos_ehci *) -			kzalloc(sizeof(struct exynos_ehci), GFP_KERNEL); -	if (!exynos) { -		debug("failed to allocate exynos ehci context\n"); -		return -ENOMEM; -	} - -	exynos_usb_parse_dt(gd->fdt_blob, exynos); - -	reset_usb_phy(exynos->usb); +	struct exynos_ehci *ctx = &exynos; -	kfree(exynos); +	reset_usb_phy(ctx->usb);  	return 0;  } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 7f98a6354..c81687820 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -21,12 +21,14 @@   * MA 02111-1307 USA   */  #include <common.h> +#include <errno.h>  #include <asm/byteorder.h>  #include <asm/unaligned.h>  #include <usb.h>  #include <asm/io.h>  #include <malloc.h>  #include <watchdog.h> +#include <linux/compiler.h>  #include "ehci.h" @@ -39,7 +41,10 @@ static struct ehci_ctrl {  	struct ehci_hcor *hcor;  	int rootdev;  	uint16_t portreset; -	struct QH qh_list __attribute__((aligned(USB_DMA_MINALIGN))); +	struct QH qh_list __aligned(USB_DMA_MINALIGN); +	struct QH periodic_queue __aligned(USB_DMA_MINALIGN); +	uint32_t *periodic_list; +	int ntds;  } ehcic[CONFIG_USB_MAX_CONTROLLER_COUNT];  #define ALIGN_END_ADDR(type, ptr, size)			\ @@ -858,6 +863,8 @@ int usb_lowlevel_init(int index, void **controller)  	uint32_t reg;  	uint32_t cmd;  	struct QH *qh_list; +	struct QH *periodic; +	int i;  	if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor))  		return -1; @@ -870,6 +877,9 @@ int usb_lowlevel_init(int index, void **controller)  	if (ehci_hcd_init(index, &ehcic[index].hccr, &ehcic[index].hcor))  		return -1;  #endif +	/* Set the high address word (aka segment) for 64-bit controller */ +	if (ehci_readl(&ehcic[index].hccr->cr_hccparams) & 1) +		ehci_writel(ehcic[index].hcor->or_ctrldssegment, 0);  	qh_list = &ehcic[index].qh_list; @@ -884,6 +894,40 @@ int usb_lowlevel_init(int index, void **controller)  	qh_list->qh_overlay.qt_token =  			cpu_to_hc32(QT_TOKEN_STATUS(QT_TOKEN_STATUS_HALTED)); +	/* Set async. queue head pointer. */ +	ehci_writel(&ehcic[index].hcor->or_asynclistaddr, (uint32_t)qh_list); + +	/* +	 * Set up periodic list +	 * Step 1: Parent QH for all periodic transfers. +	 */ +	periodic = &ehcic[index].periodic_queue; +	memset(periodic, 0, sizeof(*periodic)); +	periodic->qh_link = cpu_to_hc32(QH_LINK_TERMINATE); +	periodic->qh_overlay.qt_next = cpu_to_hc32(QT_NEXT_TERMINATE); +	periodic->qh_overlay.qt_altnext = cpu_to_hc32(QT_NEXT_TERMINATE); + +	/* +	 * Step 2: Setup frame-list: Every microframe, USB tries the same list. +	 *         In particular, device specifications on polling frequency +	 *         are disregarded. Keyboards seem to send NAK/NYet reliably +	 *         when polled with an empty buffer. +	 * +	 *         Split Transactions will be spread across microframes using +	 *         S-mask and C-mask. +	 */ +	ehcic[index].periodic_list = memalign(4096, 1024*4); +	if (!ehcic[index].periodic_list) +		return -ENOMEM; +	for (i = 0; i < 1024; i++) { +		ehcic[index].periodic_list[i] = (uint32_t)periodic +						| QH_LINK_TYPE_QH; +	} + +	/* Set periodic list base address */ +	ehci_writel(&ehcic[index].hcor->or_periodiclistbase, +		(uint32_t)ehcic[index].periodic_list); +  	reg = ehci_readl(&ehcic[index].hccr->cr_hcsparams);  	descriptor.hub.bNbrPorts = HCS_N_PORTS(reg);  	debug("Register %x NbrPorts %d\n", reg, descriptor.hub.bNbrPorts); @@ -953,10 +997,254 @@ submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,  	return ehci_submit_async(dev, pipe, buffer, length, setup);  } +struct int_queue { +	struct QH *first; +	struct QH *current; +	struct QH *last; +	struct qTD *tds; +}; + +#define NEXT_QH(qh) (struct QH *)((qh)->qh_link & ~0x1f) + +static int +enable_periodic(struct ehci_ctrl *ctrl) +{ +	uint32_t cmd; +	struct ehci_hcor *hcor = ctrl->hcor; +	int ret; + +	cmd = ehci_readl(&hcor->or_usbcmd); +	cmd |= CMD_PSE; +	ehci_writel(&hcor->or_usbcmd, cmd); + +	ret = handshake((uint32_t *)&hcor->or_usbsts, +			STS_PSS, STS_PSS, 100 * 1000); +	if (ret < 0) { +		printf("EHCI failed: timeout when enabling periodic list\n"); +		return -ETIMEDOUT; +	} +	udelay(1000); +	return 0; +} + +static int +disable_periodic(struct ehci_ctrl *ctrl) +{ +	uint32_t cmd; +	struct ehci_hcor *hcor = ctrl->hcor; +	int ret; + +	cmd = ehci_readl(&hcor->or_usbcmd); +	cmd &= ~CMD_PSE; +	ehci_writel(&hcor->or_usbcmd, cmd); + +	ret = handshake((uint32_t *)&hcor->or_usbsts, +			STS_PSS, 0, 100 * 1000); +	if (ret < 0) { +		printf("EHCI failed: timeout when disabling periodic list\n"); +		return -ETIMEDOUT; +	} +	return 0; +} + +static int periodic_schedules; + +struct int_queue * +create_int_queue(struct usb_device *dev, unsigned long pipe, int queuesize, +		 int elementsize, void *buffer) +{ +	struct ehci_ctrl *ctrl = dev->controller; +	struct int_queue *result = NULL; +	int i; + +	debug("Enter create_int_queue\n"); +	if (usb_pipetype(pipe) != PIPE_INTERRUPT) { +		debug("non-interrupt pipe (type=%lu)", usb_pipetype(pipe)); +		return NULL; +	} + +	/* limit to 4 full pages worth of data - +	 * we can safely fit them in a single TD, +	 * no matter the alignment +	 */ +	if (elementsize >= 16384) { +		debug("too large elements for interrupt transfers\n"); +		return NULL; +	} + +	result = malloc(sizeof(*result)); +	if (!result) { +		debug("ehci intr queue: out of memory\n"); +		goto fail1; +	} +	result->first = memalign(32, sizeof(struct QH) * queuesize); +	if (!result->first) { +		debug("ehci intr queue: out of memory\n"); +		goto fail2; +	} +	result->current = result->first; +	result->last = result->first + queuesize - 1; +	result->tds = memalign(32, sizeof(struct qTD) * queuesize); +	if (!result->tds) { +		debug("ehci intr queue: out of memory\n"); +		goto fail3; +	} +	memset(result->first, 0, sizeof(struct QH) * queuesize); +	memset(result->tds, 0, sizeof(struct qTD) * queuesize); + +	for (i = 0; i < queuesize; i++) { +		struct QH *qh = result->first + i; +		struct qTD *td = result->tds + i; +		void **buf = &qh->buffer; + +		qh->qh_link = (uint32_t)(qh+1) | QH_LINK_TYPE_QH; +		if (i == queuesize - 1) +			qh->qh_link = QH_LINK_TERMINATE; + +		qh->qh_overlay.qt_next = (uint32_t)td; +		qh->qh_endpt1 = (0 << 28) | /* No NAK reload (ehci 4.9) */ +			(usb_maxpacket(dev, pipe) << 16) | /* MPS */ +			(1 << 14) | +			QH_ENDPT1_EPS(ehci_encode_speed(dev->speed)) | +			(usb_pipeendpoint(pipe) << 8) | /* Endpoint Number */ +			(usb_pipedevice(pipe) << 0); +		qh->qh_endpt2 = (1 << 30) | /* 1 Tx per mframe */ +			(1 << 0); /* S-mask: microframe 0 */ +		if (dev->speed == USB_SPEED_LOW || +				dev->speed == USB_SPEED_FULL) { +			debug("TT: port: %d, hub address: %d\n", +				dev->portnr, dev->parent->devnum); +			qh->qh_endpt2 |= (dev->portnr << 23) | +				(dev->parent->devnum << 16) | +				(0x1c << 8); /* C-mask: microframes 2-4 */ +		} + +		td->qt_next = QT_NEXT_TERMINATE; +		td->qt_altnext = QT_NEXT_TERMINATE; +		debug("communication direction is '%s'\n", +		      usb_pipein(pipe) ? "in" : "out"); +		td->qt_token = (elementsize << 16) | +			((usb_pipein(pipe) ? 1 : 0) << 8) | /* IN/OUT token */ +			0x80; /* active */ +		td->qt_buffer[0] = (uint32_t)buffer + i * elementsize; +		td->qt_buffer[1] = (td->qt_buffer[0] + 0x1000) & ~0xfff; +		td->qt_buffer[2] = (td->qt_buffer[0] + 0x2000) & ~0xfff; +		td->qt_buffer[3] = (td->qt_buffer[0] + 0x3000) & ~0xfff; +		td->qt_buffer[4] = (td->qt_buffer[0] + 0x4000) & ~0xfff; + +		*buf = buffer + i * elementsize; +	} + +	if (disable_periodic(ctrl) < 0) { +		debug("FATAL: periodic should never fail, but did"); +		goto fail3; +	} + +	/* hook up to periodic list */ +	struct QH *list = &ctrl->periodic_queue; +	result->last->qh_link = list->qh_link; +	list->qh_link = (uint32_t)result->first | QH_LINK_TYPE_QH; + +	if (enable_periodic(ctrl) < 0) { +		debug("FATAL: periodic should never fail, but did"); +		goto fail3; +	} +	periodic_schedules++; + +	debug("Exit create_int_queue\n"); +	return result; +fail3: +	if (result->tds) +		free(result->tds); +fail2: +	if (result->first) +		free(result->first); +	if (result) +		free(result); +fail1: +	return NULL; +} + +void *poll_int_queue(struct usb_device *dev, struct int_queue *queue) +{ +	struct QH *cur = queue->current; + +	/* depleted queue */ +	if (cur == NULL) { +		debug("Exit poll_int_queue with completed queue\n"); +		return NULL; +	} +	/* still active */ +	if (cur->qh_overlay.qt_token & 0x80) { +		debug("Exit poll_int_queue with no completed intr transfer. " +		      "token is %x\n", cur->qh_overlay.qt_token); +		return NULL; +	} +	if (!(cur->qh_link & QH_LINK_TERMINATE)) +		queue->current++; +	else +		queue->current = NULL; +	debug("Exit poll_int_queue with completed intr transfer. " +	      "token is %x at %p (first at %p)\n", cur->qh_overlay.qt_token, +	      &cur->qh_overlay.qt_token, queue->first); +	return cur->buffer; +} + +/* Do not free buffers associated with QHs, they're owned by someone else */ +int +destroy_int_queue(struct usb_device *dev, struct int_queue *queue) +{ +	struct ehci_ctrl *ctrl = dev->controller; +	int result = -1; +	unsigned long timeout; + +	if (disable_periodic(ctrl) < 0) { +		debug("FATAL: periodic should never fail, but did"); +		goto out; +	} +	periodic_schedules--; + +	struct QH *cur = &ctrl->periodic_queue; +	timeout = get_timer(0) + 500; /* abort after 500ms */ +	while (!(cur->qh_link & QH_LINK_TERMINATE)) { +		debug("considering %p, with qh_link %x\n", cur, cur->qh_link); +		if (NEXT_QH(cur) == queue->first) { +			debug("found candidate. removing from chain\n"); +			cur->qh_link = queue->last->qh_link; +			result = 0; +			break; +		} +		cur = NEXT_QH(cur); +		if (get_timer(0) > timeout) { +			printf("Timeout destroying interrupt endpoint queue\n"); +			result = -1; +			goto out; +		} +	} + +	if (periodic_schedules > 0) { +		result = enable_periodic(ctrl); +		if (result < 0) +			debug("FATAL: periodic should never fail, but did"); +	} + +out: +	free(queue->tds); +	free(queue->first); +	free(queue); + +	return result; +} +  int  submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,  	       int length, int interval)  { +	void *backbuffer; +	struct int_queue *queue; +	unsigned long timeout; +	int result = 0, ret; +  	debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",  	      dev, pipe, buffer, length, interval); @@ -972,9 +1260,31 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,  	 * not require more than a single qTD.  	 */  	if (length > usb_maxpacket(dev, pipe)) { -		printf("%s: Interrupt transfers requiring several transactions " -			"are not supported.\n", __func__); +		printf("%s: Interrupt transfers requiring several " +			"transactions are not supported.\n", __func__);  		return -1;  	} -	return ehci_submit_async(dev, pipe, buffer, length, NULL); + +	queue = create_int_queue(dev, pipe, 1, length, buffer); + +	timeout = get_timer(0) + USB_TIMEOUT_MS(pipe); +	while ((backbuffer = poll_int_queue(dev, queue)) == NULL) +		if (get_timer(0) > timeout) { +			printf("Timeout poll on interrupt endpoint\n"); +			result = -ETIMEDOUT; +			break; +		} + +	if (backbuffer != buffer) { +		debug("got wrong buffer back (%x instead of %x)\n", +		      (uint32_t)backbuffer, (uint32_t)buffer); +		return -EINVAL; +	} + +	ret = destroy_int_queue(dev, queue); +	if (ret < 0) +		return ret; + +	/* everything worked out fine */ +	return result;  } diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c index 1b20e4185..c1ae3d908 100644 --- a/drivers/usb/host/ehci-mx6.c +++ b/drivers/usb/host/ehci-mx6.c @@ -21,7 +21,6 @@  #include <asm/io.h>  #include <asm/arch/imx-regs.h>  #include <asm/arch/clock.h> -#include <asm/arch/mx6x_pins.h>  #include <asm/imx-common/iomux-v3.h>  #include "ehci.h" diff --git a/drivers/usb/host/ehci-mxs.c b/drivers/usb/host/ehci-mxs.c index 5062af555..f320d3eb5 100644 --- a/drivers/usb/host/ehci-mxs.c +++ b/drivers/usb/host/ehci-mxs.c @@ -21,91 +21,107 @@  #include <common.h>  #include <asm/io.h> -#include <asm/arch/regs-common.h> -#include <asm/arch/regs-base.h> -#include <asm/arch/regs-clkctrl-mx28.h> -#include <asm/arch/regs-usb.h> -#include <asm/arch/regs-usbphy.h> +#include <asm/arch/imx-regs.h> +#include <errno.h>  #include "ehci.h" -#if	(CONFIG_EHCI_MXS_PORT != 0) && (CONFIG_EHCI_MXS_PORT != 1) -#error	"MXS EHCI: Invalid port selected!" -#endif - -#ifndef	CONFIG_EHCI_MXS_PORT -#error	"MXS EHCI: Please define correct port using CONFIG_EHCI_MXS_PORT!" -#endif +/* This DIGCTL register ungates clock to USB */ +#define	HW_DIGCTL_CTRL			0x8001c000 +#define	HW_DIGCTL_CTRL_USB0_CLKGATE	(1 << 2) +#define	HW_DIGCTL_CTRL_USB1_CLKGATE	(1 << 16) -static struct ehci_mxs { -	struct mxs_usb_regs	*usb_regs; +struct ehci_mxs_port { +	uint32_t		usb_regs;  	struct mxs_usbphy_regs	*phy_regs; -} ehci_mxs; -int mxs_ehci_get_port(struct ehci_mxs *mxs_usb, int port) +	struct mxs_register_32	*pll; +	uint32_t		pll_en_bits; +	uint32_t		pll_dis_bits; +	uint32_t		gate_bits; +}; + +static const struct ehci_mxs_port mxs_port[] = { +#ifdef CONFIG_EHCI_MXS_PORT0 +	{ +		MXS_USBCTRL0_BASE, +		(struct mxs_usbphy_regs *)MXS_USBPHY0_BASE, +		(struct mxs_register_32 *)(MXS_CLKCTRL_BASE + +			offsetof(struct mxs_clkctrl_regs, +			hw_clkctrl_pll0ctrl0_reg)), +		CLKCTRL_PLL0CTRL0_EN_USB_CLKS | CLKCTRL_PLL0CTRL0_POWER, +		CLKCTRL_PLL0CTRL0_EN_USB_CLKS, +		HW_DIGCTL_CTRL_USB0_CLKGATE, +	}, +#endif +#ifdef CONFIG_EHCI_MXS_PORT1 +	{ +		MXS_USBCTRL1_BASE, +		(struct mxs_usbphy_regs *)MXS_USBPHY1_BASE, +		(struct mxs_register_32 *)(MXS_CLKCTRL_BASE + +			offsetof(struct mxs_clkctrl_regs, +			hw_clkctrl_pll1ctrl0_reg)), +		CLKCTRL_PLL1CTRL0_EN_USB_CLKS | CLKCTRL_PLL1CTRL0_POWER, +		CLKCTRL_PLL1CTRL0_EN_USB_CLKS, +		HW_DIGCTL_CTRL_USB1_CLKGATE, +	}, +#endif +}; + +static int ehci_mxs_toggle_clock(const struct ehci_mxs_port *port, int enable)  { -	uint32_t usb_base, phy_base; -	switch (port) { -	case 0: -		usb_base = MXS_USBCTRL0_BASE; -		phy_base = MXS_USBPHY0_BASE; -		break; -	case 1: -		usb_base = MXS_USBCTRL1_BASE; -		phy_base = MXS_USBPHY1_BASE; -		break; -	default: -		printf("CONFIG_EHCI_MXS_PORT (port = %d)\n", port); -		return -1; +	struct mxs_register_32 *digctl_ctrl = +		(struct mxs_register_32 *)HW_DIGCTL_CTRL; +	int pll_offset, dig_offset; + +	if (enable) { +		pll_offset = offsetof(struct mxs_register_32, reg_set); +		dig_offset = offsetof(struct mxs_register_32, reg_clr); +		writel(port->gate_bits, (u32)&digctl_ctrl->reg + dig_offset); +		writel(port->pll_en_bits, (u32)port->pll + pll_offset); +	} else { +		pll_offset = offsetof(struct mxs_register_32, reg_clr); +		dig_offset = offsetof(struct mxs_register_32, reg_set); +		writel(port->pll_dis_bits, (u32)port->pll + pll_offset); +		writel(port->gate_bits, (u32)&digctl_ctrl->reg + dig_offset);  	} -	mxs_usb->usb_regs = (struct mxs_usb_regs *)usb_base; -	mxs_usb->phy_regs = (struct mxs_usbphy_regs *)phy_base;  	return 0;  } -/* This DIGCTL register ungates clock to USB */ -#define	HW_DIGCTL_CTRL			0x8001c000 -#define	HW_DIGCTL_CTRL_USB0_CLKGATE	(1 << 2) -#define	HW_DIGCTL_CTRL_USB1_CLKGATE	(1 << 16) -  int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor)  {  	int ret;  	uint32_t usb_base, cap_base; -	struct mxs_register_32 *digctl_ctrl = -		(struct mxs_register_32 *)HW_DIGCTL_CTRL; -	struct mxs_clkctrl_regs *clkctrl_regs = -		(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; +	const struct ehci_mxs_port *port; -	ret = mxs_ehci_get_port(&ehci_mxs, CONFIG_EHCI_MXS_PORT); -	if (ret) -		return ret; +	if ((index < 0) || (index >= ARRAY_SIZE(mxs_port))) { +		printf("Invalid port index (index = %d)!\n", index); +		return -EINVAL; +	} + +	port = &mxs_port[index];  	/* Reset the PHY block */ -	writel(USBPHY_CTRL_SFTRST, &ehci_mxs.phy_regs->hw_usbphy_ctrl_set); +	writel(USBPHY_CTRL_SFTRST, &port->phy_regs->hw_usbphy_ctrl_set);  	udelay(10);  	writel(USBPHY_CTRL_SFTRST | USBPHY_CTRL_CLKGATE, -		&ehci_mxs.phy_regs->hw_usbphy_ctrl_clr); +		&port->phy_regs->hw_usbphy_ctrl_clr);  	/* Enable USB clock */ -	writel(CLKCTRL_PLL0CTRL0_EN_USB_CLKS | CLKCTRL_PLL0CTRL0_POWER, -			&clkctrl_regs->hw_clkctrl_pll0ctrl0_set); -	writel(CLKCTRL_PLL1CTRL0_EN_USB_CLKS | CLKCTRL_PLL1CTRL0_POWER, -			&clkctrl_regs->hw_clkctrl_pll1ctrl0_set); - -	writel(HW_DIGCTL_CTRL_USB0_CLKGATE | HW_DIGCTL_CTRL_USB1_CLKGATE, -		&digctl_ctrl->reg_clr); +	ret = ehci_mxs_toggle_clock(port, 1); +	if (ret) +		return ret;  	/* Start USB PHY */ -	writel(0, &ehci_mxs.phy_regs->hw_usbphy_pwd); +	writel(0, &port->phy_regs->hw_usbphy_pwd);  	/* Enable UTMI+ Level 2 and Level 3 compatibility */  	writel(USBPHY_CTRL_ENUTMILEVEL3 | USBPHY_CTRL_ENUTMILEVEL2 | 1, -		&ehci_mxs.phy_regs->hw_usbphy_ctrl_set); +		&port->phy_regs->hw_usbphy_ctrl_set); -	usb_base = ((uint32_t)ehci_mxs.usb_regs) + 0x100; +	usb_base = port->usb_regs + 0x100;  	*hccr = (struct ehci_hccr *)usb_base;  	cap_base = ehci_readl(&(*hccr)->cr_capbase); @@ -118,19 +134,19 @@ int ehci_hcd_stop(int index)  {  	int ret;  	uint32_t usb_base, cap_base, tmp; -	struct mxs_register_32 *digctl_ctrl = -		(struct mxs_register_32 *)HW_DIGCTL_CTRL; -	struct mxs_clkctrl_regs *clkctrl_regs = -		(struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;  	struct ehci_hccr *hccr;  	struct ehci_hcor *hcor; +	const struct ehci_mxs_port *port; -	ret = mxs_ehci_get_port(&ehci_mxs, CONFIG_EHCI_MXS_PORT); -	if (ret) -		return ret; +	if ((index < 0) || (index >= ARRAY_SIZE(mxs_port))) { +		printf("Invalid port index (index = %d)!\n", index); +		return -EINVAL; +	} + +	port = &mxs_port[index];  	/* Stop the USB port */ -	usb_base = ((uint32_t)ehci_mxs.usb_regs) + 0x100; +	usb_base = port->usb_regs + 0x100;  	hccr = (struct ehci_hccr *)usb_base;  	cap_base = ehci_readl(&hccr->cr_capbase);  	hcor = (struct ehci_hcor *)(usb_base + HC_LENGTH(cap_base)); @@ -144,17 +160,10 @@ int ehci_hcd_stop(int index)  		USBPHY_PWD_RXPWD1PT1 | USBPHY_PWD_RXPWDENV |  		USBPHY_PWD_TXPWDV2I | USBPHY_PWD_TXPWDIBIAS |  		USBPHY_PWD_TXPWDFS; -	writel(tmp, &ehci_mxs.phy_regs->hw_usbphy_pwd); +	writel(tmp, &port->phy_regs->hw_usbphy_pwd);  	/* Disable USB clock */ -	writel(CLKCTRL_PLL0CTRL0_EN_USB_CLKS, -			&clkctrl_regs->hw_clkctrl_pll0ctrl0_clr); -	writel(CLKCTRL_PLL1CTRL0_EN_USB_CLKS, -			&clkctrl_regs->hw_clkctrl_pll1ctrl0_clr); +	ret = ehci_mxs_toggle_clock(port, 0); -	/* Gate off the USB clock */ -	writel(HW_DIGCTL_CTRL_USB0_CLKGATE | HW_DIGCTL_CTRL_USB1_CLKGATE, -		&digctl_ctrl->reg_set); - -	return 0; +	return ret;  } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 29af02dc5..90d7a6feb 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -19,6 +19,7 @@   */  #include <common.h> +#include <errno.h>  #include <pci.h>  #include <usb.h> @@ -32,31 +33,76 @@ static struct pci_device_id ehci_pci_ids[] = {  	{0x12D8, 0x400F},	/* Pericom */  	{0, 0}  }; +#else +static pci_dev_t ehci_find_class(int index) +{ +	int bus; +	int devnum; +	pci_dev_t bdf; +	uint32_t class; + +	for (bus = 0; bus <= pci_last_busno(); bus++) { +		for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES-1; devnum++) { +			pci_read_config_dword(PCI_BDF(bus, devnum, 0), +					      PCI_CLASS_REVISION, &class); +			if (class >> 16 == 0xffff) +				continue; + +			for (bdf = PCI_BDF(bus, devnum, 0); +					bdf <= PCI_BDF(bus, devnum, +						PCI_MAX_PCI_FUNCTIONS - 1); +					bdf += PCI_BDF(0, 0, 1)) { +				pci_read_config_dword(bdf, PCI_CLASS_REVISION, +						      &class); +				if ((class >> 8 == PCI_CLASS_SERIAL_USB_EHCI) +						&& !index--) +					return bdf; +			} +		} +	} + +	return -ENODEV; +}  #endif  /*   * Create the appropriate control structures to manage   * a new EHCI host controller.   */ -int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor) +int ehci_hcd_init(int index, struct ehci_hccr **ret_hccr, +		struct ehci_hcor **ret_hcor)  {  	pci_dev_t pdev; +	uint32_t cmd; +	struct ehci_hccr *hccr; +	struct ehci_hcor *hcor; +#ifdef CONFIG_PCI_EHCI_DEVICE  	pdev = pci_find_devices(ehci_pci_ids, CONFIG_PCI_EHCI_DEVICE); -	if (pdev == -1) { +#else +	pdev = ehci_find_class(index); +#endif +	if (pdev < 0) {  		printf("EHCI host controller not found\n");  		return -1;  	} -	*hccr = (struct ehci_hccr *)pci_map_bar(pdev, +	hccr = (struct ehci_hccr *)pci_map_bar(pdev,  			PCI_BASE_ADDRESS_0, PCI_REGION_MEM); -	*hcor = (struct ehci_hcor *)((uint32_t) *hccr + -			HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); +	hcor = (struct ehci_hcor *)((uint32_t) hccr + +			HC_LENGTH(ehci_readl(&hccr->cr_capbase)));  	debug("EHCI-PCI init hccr 0x%x and hcor 0x%x hc_length %d\n", -			(uint32_t)*hccr, (uint32_t)*hcor, -			(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); +			(uint32_t)hccr, (uint32_t)hcor, +			(uint32_t)HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + +	*ret_hccr = hccr; +	*ret_hcor = hcor; +	/* enable busmaster */ +	pci_read_config_dword(pdev, PCI_COMMAND, &cmd); +	cmd |= PCI_COMMAND_MASTER; +	pci_write_config_dword(pdev, PCI_COMMAND, cmd);  	return 0;  } diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c new file mode 100644 index 000000000..f99bd1f69 --- /dev/null +++ b/drivers/usb/host/ehci-spear.c @@ -0,0 +1,59 @@ +/* + * (C) Copyright 2010 + * Armando Visconti, ST Micoelectronics, <armando.visconti@st.com>. + * + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Written-by: Prafulla Wadaskar <prafulla@marvell.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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 Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <usb.h> +#include "ehci.h" +#include <asm/arch/hardware.h> + + +/* + * Create the appropriate control structures to manage + * a new EHCI host controller. + */ +int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	*hccr = (struct ehci_hccr *)(CONFIG_SYS_UHC0_EHCI_BASE + 0x100); +	*hcor = (struct ehci_hcor *)((uint32_t)*hccr +			+ HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	debug("SPEAr-ehci: init hccr %x and hcor %x hc_length %d\n", +		(uint32_t)*hccr, (uint32_t)*hcor, +		(uint32_t)HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); + +	return 0; +} + +/* + * Destroy the appropriate control structures corresponding + * the the EHCI host controller. + */ +int ehci_hcd_stop(int index) +{ +	return 0; +} diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index a1c43f833..554145a25 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -1,5 +1,7 @@  /* + * Copyright (c) 2011 The Chromium OS Authors.   * Copyright (c) 2009-2012 NVIDIA Corporation + * Copyright (c) 2013 Lucas Stach   *   * See file CREDITS for list of people who contributed to this   * project. @@ -21,12 +23,128 @@   */  #include <common.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm-generic/gpio.h> +#include <asm/arch/clock.h> +#include <asm/arch-tegra/usb.h>  #include <usb.h> +#include <usb/ulpi.h> +#include <libfdt.h> +#include <fdtdec.h>  #include "ehci.h" -#include <asm/errno.h> -#include <asm/arch/usb.h> +#ifdef CONFIG_USB_ULPI +	#ifndef CONFIG_USB_ULPI_VIEWPORT +	#error	"To use CONFIG_USB_ULPI on Tegra Boards you have to also \ +		define CONFIG_USB_ULPI_VIEWPORT" +	#endif +#endif + +enum { +	USB_PORTS_MAX	= 3,		/* Maximum ports we allow */ +}; + +/* Parameters we need for USB */ +enum { +	PARAM_DIVN,                     /* PLL FEEDBACK DIVIDer */ +	PARAM_DIVM,                     /* PLL INPUT DIVIDER */ +	PARAM_DIVP,                     /* POST DIVIDER (2^N) */ +	PARAM_CPCON,                    /* BASE PLLC CHARGE Pump setup ctrl */ +	PARAM_LFCON,                    /* BASE PLLC LOOP FILter setup ctrl */ +	PARAM_ENABLE_DELAY_COUNT,       /* PLL-U Enable Delay Count */ +	PARAM_STABLE_COUNT,             /* PLL-U STABLE count */ +	PARAM_ACTIVE_DELAY_COUNT,       /* PLL-U Active delay count */ +	PARAM_XTAL_FREQ_COUNT,          /* PLL-U XTAL frequency count */ +	PARAM_DEBOUNCE_A_TIME,          /* 10MS DELAY for BIAS_DEBOUNCE_A */ +	PARAM_BIAS_TIME,                /* 20US DELAY AFter bias cell op */ + +	PARAM_COUNT +}; + +/* Possible port types (dual role mode) */ +enum dr_mode { +	DR_MODE_NONE = 0, +	DR_MODE_HOST,		/* supports host operation */ +	DR_MODE_DEVICE,		/* supports device operation */ +	DR_MODE_OTG,		/* supports both */ +}; + +/* Information about a USB port */ +struct fdt_usb { +	struct usb_ctlr *reg;	/* address of registers in physical memory */ +	unsigned utmi:1;	/* 1 if port has external tranceiver, else 0 */ +	unsigned ulpi:1;	/* 1 if port has external ULPI transceiver */ +	unsigned enabled:1;	/* 1 to enable, 0 to disable */ +	unsigned has_legacy_mode:1; /* 1 if this port has legacy mode */ +	unsigned initialized:1; /* has this port already been initialized? */ +	enum dr_mode dr_mode;	/* dual role mode */ +	enum periph_id periph_id;/* peripheral id */ +	struct fdt_gpio_state vbus_gpio;	/* GPIO for vbus enable */ +	struct fdt_gpio_state phy_reset_gpio; /* GPIO to reset ULPI phy */ +}; + +static struct fdt_usb port[USB_PORTS_MAX];	/* List of valid USB ports */ +static unsigned port_count;			/* Number of available ports */ + +/* + * This table has USB timing parameters for each Oscillator frequency we + * support. There are four sets of values: + * + * 1. PLLU configuration information (reference clock is osc/clk_m and + * PLLU-FOs are fixed at 12MHz/60MHz/480MHz). + * + *  Reference frequency     13.0MHz      19.2MHz      12.0MHz      26.0MHz + *  ---------------------------------------------------------------------- + *      DIVN                960 (0x3c0)  200 (0c8)    960 (3c0h)   960 (3c0) + *      DIVM                13 (0d)      4 (04)       12 (0c)      26 (1a) + * Filter frequency (MHz)   1            4.8          6            2 + * CPCON                    1100b        0011b        1100b        1100b + * LFCON0                   0            0            0            0 + * + * 2. PLL CONFIGURATION & PARAMETERS for different clock generators: + * + * Reference frequency     13.0MHz         19.2MHz         12.0MHz     26.0MHz + * --------------------------------------------------------------------------- + * PLLU_ENABLE_DLY_COUNT   02 (0x02)       03 (03)         02 (02)     04 (04) + * PLLU_STABLE_COUNT       51 (33)         75 (4B)         47 (2F)    102 (66) + * PLL_ACTIVE_DLY_COUNT    05 (05)         06 (06)         04 (04)     09 (09) + * XTAL_FREQ_COUNT        127 (7F)        187 (BB)        118 (76)    254 (FE) + * + * 3. Debounce values IdDig, Avalid, Bvalid, VbusValid, VbusWakeUp, and + * SessEnd. Each of these signals have their own debouncer and for each of + * those one out of two debouncing times can be chosen (BIAS_DEBOUNCE_A or + * BIAS_DEBOUNCE_B). + * + * The values of DEBOUNCE_A and DEBOUNCE_B are calculated as follows: + *    0xffff -> No debouncing at all + *    <n> ms = <n> *1000 / (1/19.2MHz) / 4 + * + * So to program a 1 ms debounce for BIAS_DEBOUNCE_A, we have: + * BIAS_DEBOUNCE_A[15:0] = 1000 * 19.2 / 4  = 4800 = 0x12c0 + * + * We need to use only DebounceA for BOOTROM. We don't need the DebounceB + * values, so we can keep those to default. + * + * 4. The 20 microsecond delay after bias cell operation. + */ +static const unsigned usb_pll[CLOCK_OSC_FREQ_COUNT][PARAM_COUNT] = { +	/* DivN, DivM, DivP, CPCON, LFCON, Delays             Debounce, Bias */ +	{ 0x3C0, 0x0D, 0x00, 0xC,   0,  0x02, 0x33, 0x05, 0x7F, 0x7EF4, 5 }, +	{ 0x0C8, 0x04, 0x00, 0x3,   0,  0x03, 0x4B, 0x06, 0xBB, 0xBB80, 7 }, +	{ 0x3C0, 0x0C, 0x00, 0xC,   0,  0x02, 0x2F, 0x04, 0x76, 0x7530, 5 }, +	{ 0x3C0, 0x1A, 0x00, 0xC,   0,  0x04, 0x66, 0x09, 0xFE, 0xFDE8, 9 } +}; + +/* UTMIP Idle Wait Delay */ +static const u8 utmip_idle_wait_delay = 17; + +/* UTMIP Elastic limit */ +static const u8 utmip_elastic_limit = 16; + +/* UTMIP High Speed Sync Start Delay */ +static const u8 utmip_hs_sync_start_delay = 9;  /*   * A known hardware issue where Connect Status Change bit of PORTSC register @@ -45,32 +163,428 @@ void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg)  		*reg |= EHCI_PS_CSC;  } -/* - * Create the appropriate control structures to manage - * a new EHCI host controller. - */ -int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor) +/* Put the port into host mode */ +static void set_host_mode(struct fdt_usb *config) +{ +	/* +	 * If we are an OTG port, check if remote host is driving VBus and +	 * bail out in this case. +	 */ +	if (config->dr_mode == DR_MODE_OTG && +		(readl(&config->reg->phy_vbus_sensors) & VBUS_VLD_STS)) +		return; + +	/* +	 * If not driving, we set the GPIO to enable VBUS. We assume +	 * that the pinmux is set up correctly for this. +	 */ +	if (fdt_gpio_isvalid(&config->vbus_gpio)) { +		fdtdec_setup_gpio(&config->vbus_gpio); +		gpio_direction_output(config->vbus_gpio.gpio, +			(config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ? +				 0 : 1); +		debug("set_host_mode: GPIO %d %s\n", config->vbus_gpio.gpio, +			(config->vbus_gpio.flags & FDT_GPIO_ACTIVE_LOW) ? +				"low" : "high"); +	} +} + +void usbf_reset_controller(struct fdt_usb *config, struct usb_ctlr *usbctlr) +{ +	/* Reset the USB controller with 2us delay */ +	reset_periph(config->periph_id, 2); + +	/* +	 * Set USB1_NO_LEGACY_MODE to 1, Registers are accessible under +	 * base address +	 */ +	if (config->has_legacy_mode) +		setbits_le32(&usbctlr->usb1_legacy_ctrl, USB1_NO_LEGACY_MODE); + +	/* Put UTMIP1/3 in reset */ +	setbits_le32(&usbctlr->susp_ctrl, UTMIP_RESET); + +	/* Enable the UTMIP PHY */ +	if (config->utmi) +		setbits_le32(&usbctlr->susp_ctrl, UTMIP_PHY_ENB); +} + +/* set up the UTMI USB controller with the parameters provided */ +static int init_utmi_usb_controller(struct fdt_usb *config)  { -	u32 our_hccr, our_hcor; +	u32 val; +	int loop_count; +	const unsigned *timing; +	struct usb_ctlr *usbctlr = config->reg; + +	clock_enable(config->periph_id); + +	/* Reset the usb controller */ +	usbf_reset_controller(config, usbctlr); + +	/* Stop crystal clock by setting UTMIP_PHY_XTAL_CLOCKEN low */ +	clrbits_le32(&usbctlr->utmip_misc_cfg1, UTMIP_PHY_XTAL_CLOCKEN); + +	/* Follow the crystal clock disable by >100ns delay */ +	udelay(1); + +	/* +	 * To Use the A Session Valid for cable detection logic, VBUS_WAKEUP +	 * mux must be switched to actually use a_sess_vld threshold. +	 */ +	if (fdt_gpio_isvalid(&config->vbus_gpio)) { +		clrsetbits_le32(&usbctlr->usb1_legacy_ctrl, +			VBUS_SENSE_CTL_MASK, +			VBUS_SENSE_CTL_A_SESS_VLD << VBUS_SENSE_CTL_SHIFT); +	} + +	/* +	 * PLL Delay CONFIGURATION settings. The following parameters control +	 * the bring up of the plls. +	 */ +	timing = usb_pll[clock_get_osc_freq()]; + +	val = readl(&usbctlr->utmip_misc_cfg1); +	clrsetbits_le32(&val, UTMIP_PLLU_STABLE_COUNT_MASK, +		timing[PARAM_STABLE_COUNT] << UTMIP_PLLU_STABLE_COUNT_SHIFT); +	clrsetbits_le32(&val, UTMIP_PLL_ACTIVE_DLY_COUNT_MASK, +		timing[PARAM_ACTIVE_DELAY_COUNT] << +			UTMIP_PLL_ACTIVE_DLY_COUNT_SHIFT); +	writel(val, &usbctlr->utmip_misc_cfg1); + +	/* Set PLL enable delay count and crystal frequency count */ +	val = readl(&usbctlr->utmip_pll_cfg1); +	clrsetbits_le32(&val, UTMIP_PLLU_ENABLE_DLY_COUNT_MASK, +		timing[PARAM_ENABLE_DELAY_COUNT] << +			UTMIP_PLLU_ENABLE_DLY_COUNT_SHIFT); +	clrsetbits_le32(&val, UTMIP_XTAL_FREQ_COUNT_MASK, +		timing[PARAM_XTAL_FREQ_COUNT] << +			UTMIP_XTAL_FREQ_COUNT_SHIFT); +	writel(val, &usbctlr->utmip_pll_cfg1); + +	/* Setting the tracking length time */ +	clrsetbits_le32(&usbctlr->utmip_bias_cfg1, +		UTMIP_BIAS_PDTRK_COUNT_MASK, +		timing[PARAM_BIAS_TIME] << UTMIP_BIAS_PDTRK_COUNT_SHIFT); + +	/* Program debounce time for VBUS to become valid */ +	clrsetbits_le32(&usbctlr->utmip_debounce_cfg0, +		UTMIP_DEBOUNCE_CFG0_MASK, +		timing[PARAM_DEBOUNCE_A_TIME] << UTMIP_DEBOUNCE_CFG0_SHIFT); + +	setbits_le32(&usbctlr->utmip_tx_cfg0, UTMIP_FS_PREAMBLE_J); + +	/* Disable battery charge enabling bit */ +	setbits_le32(&usbctlr->utmip_bat_chrg_cfg0, UTMIP_PD_CHRG); + +	clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_XCVR_LSBIAS_SE); +	setbits_le32(&usbctlr->utmip_spare_cfg0, FUSE_SETUP_SEL);  	/* -	 * Select the first port, as we don't have a way of selecting others -	 * yet +	 * Configure the UTMIP_IDLE_WAIT and UTMIP_ELASTIC_LIMIT +	 * Setting these fields, together with default values of the +	 * other fields, results in programming the registers below as +	 * follows: +	 *         UTMIP_HSRX_CFG0 = 0x9168c000 +	 *         UTMIP_HSRX_CFG1 = 0x13  	 */ -	if (tegrausb_start_port(index, &our_hccr, &our_hcor)) + +	/* Set PLL enable delay count and Crystal frequency count */ +	val = readl(&usbctlr->utmip_hsrx_cfg0); +	clrsetbits_le32(&val, UTMIP_IDLE_WAIT_MASK, +		utmip_idle_wait_delay << UTMIP_IDLE_WAIT_SHIFT); +	clrsetbits_le32(&val, UTMIP_ELASTIC_LIMIT_MASK, +		utmip_elastic_limit << UTMIP_ELASTIC_LIMIT_SHIFT); +	writel(val, &usbctlr->utmip_hsrx_cfg0); + +	/* Configure the UTMIP_HS_SYNC_START_DLY */ +	clrsetbits_le32(&usbctlr->utmip_hsrx_cfg1, +		UTMIP_HS_SYNC_START_DLY_MASK, +		utmip_hs_sync_start_delay << UTMIP_HS_SYNC_START_DLY_SHIFT); + +	/* Preceed the crystal clock disable by >100ns delay. */ +	udelay(1); + +	/* Resuscitate crystal clock by setting UTMIP_PHY_XTAL_CLOCKEN */ +	setbits_le32(&usbctlr->utmip_misc_cfg1, UTMIP_PHY_XTAL_CLOCKEN); + +	/* Finished the per-controller init. */ + +	/* De-assert UTMIP_RESET to bring out of reset. */ +	clrbits_le32(&usbctlr->susp_ctrl, UTMIP_RESET); + +	/* Wait for the phy clock to become valid in 100 ms */ +	for (loop_count = 100000; loop_count != 0; loop_count--) { +		if (readl(&usbctlr->susp_ctrl) & USB_PHY_CLK_VALID) +			break; +		udelay(1); +	} +	if (!loop_count) +		return -1; + +	/* Disable ICUSB FS/LS transceiver */ +	clrbits_le32(&usbctlr->icusb_ctrl, IC_ENB1); + +	/* Select UTMI parallel interface */ +	clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK, +			PTS_UTMI << PTS_SHIFT); +	clrbits_le32(&usbctlr->port_sc1, STS); + +	/* Deassert power down state */ +	clrbits_le32(&usbctlr->utmip_xcvr_cfg0, UTMIP_FORCE_PD_POWERDOWN | +		UTMIP_FORCE_PD2_POWERDOWN | UTMIP_FORCE_PDZI_POWERDOWN); +	clrbits_le32(&usbctlr->utmip_xcvr_cfg1, UTMIP_FORCE_PDDISC_POWERDOWN | +		UTMIP_FORCE_PDCHRP_POWERDOWN | UTMIP_FORCE_PDDR_POWERDOWN); + +	return 0; +} + +#ifdef CONFIG_USB_ULPI +/* if board file does not set a ULPI reference frequency we default to 24MHz */ +#ifndef CONFIG_ULPI_REF_CLK +#define CONFIG_ULPI_REF_CLK 24000000 +#endif + +/* set up the ULPI USB controller with the parameters provided */ +static int init_ulpi_usb_controller(struct fdt_usb *config) +{ +	u32 val; +	int loop_count; +	struct ulpi_viewport ulpi_vp; +	struct usb_ctlr *usbctlr = config->reg; + +	/* set up ULPI reference clock on pllp_out4 */ +	clock_enable(PERIPH_ID_DEV2_OUT); +	clock_set_pllout(CLOCK_ID_PERIPH, PLL_OUT4, CONFIG_ULPI_REF_CLK); + +	/* reset ULPI phy */ +	if (fdt_gpio_isvalid(&config->phy_reset_gpio)) { +		fdtdec_setup_gpio(&config->phy_reset_gpio); +		gpio_direction_output(config->phy_reset_gpio.gpio, 0); +		mdelay(5); +		gpio_set_value(config->phy_reset_gpio.gpio, 1); +	} + +	/* Reset the usb controller */ +	clock_enable(config->periph_id); +	usbf_reset_controller(config, usbctlr); + +	/* enable pinmux bypass */ +	setbits_le32(&usbctlr->ulpi_timing_ctrl_0, +			ULPI_CLKOUT_PINMUX_BYP | ULPI_OUTPUT_PINMUX_BYP); + +	/* Select ULPI parallel interface */ +	clrsetbits_le32(&usbctlr->port_sc1, PTS_MASK, PTS_ULPI << PTS_SHIFT); + +	/* enable ULPI transceiver */ +	setbits_le32(&usbctlr->susp_ctrl, ULPI_PHY_ENB); + +	/* configure ULPI transceiver timings */ +	val = 0; +	writel(val, &usbctlr->ulpi_timing_ctrl_1); + +	val |= ULPI_DATA_TRIMMER_SEL(4); +	val |= ULPI_STPDIRNXT_TRIMMER_SEL(4); +	val |= ULPI_DIR_TRIMMER_SEL(4); +	writel(val, &usbctlr->ulpi_timing_ctrl_1); +	udelay(10); + +	val |= ULPI_DATA_TRIMMER_LOAD; +	val |= ULPI_STPDIRNXT_TRIMMER_LOAD; +	val |= ULPI_DIR_TRIMMER_LOAD; +	writel(val, &usbctlr->ulpi_timing_ctrl_1); + +	/* set up phy for host operation with external vbus supply */ +	ulpi_vp.port_num = 0; +	ulpi_vp.viewport_addr = (u32)&usbctlr->ulpi_viewport; + +	if (ulpi_init(&ulpi_vp)) { +		printf("Tegra ULPI viewport init failed\n"); +		return -1; +	} + +	ulpi_set_vbus(&ulpi_vp, 1, 1); +	ulpi_set_vbus_indicator(&ulpi_vp, 1, 1, 0); + +	/* enable wakeup events */ +	setbits_le32(&usbctlr->port_sc1, WKCN | WKDS | WKOC); + +	/* Enable and wait for the phy clock to become valid in 100 ms */ +	setbits_le32(&usbctlr->susp_ctrl, USB_SUSP_CLR); +	for (loop_count = 100000; loop_count != 0; loop_count--) { +		if (readl(&usbctlr->susp_ctrl) & USB_PHY_CLK_VALID) +			break; +		udelay(1); +	} +	if (!loop_count) +		return -1; +	clrbits_le32(&usbctlr->susp_ctrl, USB_SUSP_CLR); + +	return 0; +} +#else +static int init_ulpi_usb_controller(struct fdt_usb *config) +{ +	printf("No code to set up ULPI controller, please enable" +			"CONFIG_USB_ULPI and CONFIG_USB_ULPI_VIEWPORT"); +	return -1; +} +#endif + +static void config_clock(const u32 timing[]) +{ +	clock_start_pll(CLOCK_ID_USB, +		timing[PARAM_DIVM], timing[PARAM_DIVN], timing[PARAM_DIVP], +		timing[PARAM_CPCON], timing[PARAM_LFCON]); +} + +int fdt_decode_usb(const void *blob, int node, struct fdt_usb *config) +{ +	const char *phy, *mode; + +	config->reg = (struct usb_ctlr *)fdtdec_get_addr(blob, node, "reg"); +	mode = fdt_getprop(blob, node, "dr_mode", NULL); +	if (mode) { +		if (0 == strcmp(mode, "host")) +			config->dr_mode = DR_MODE_HOST; +		else if (0 == strcmp(mode, "peripheral")) +			config->dr_mode = DR_MODE_DEVICE; +		else if (0 == strcmp(mode, "otg")) +			config->dr_mode = DR_MODE_OTG; +		else { +			debug("%s: Cannot decode dr_mode '%s'\n", __func__, +			      mode); +			return -FDT_ERR_NOTFOUND; +		} +	} else { +		config->dr_mode = DR_MODE_HOST; +	} + +	phy = fdt_getprop(blob, node, "phy_type", NULL); +	config->utmi = phy && 0 == strcmp("utmi", phy); +	config->ulpi = phy && 0 == strcmp("ulpi", phy); +	config->enabled = fdtdec_get_is_enabled(blob, node); +	config->has_legacy_mode = fdtdec_get_bool(blob, node, +						  "nvidia,has-legacy-mode"); +	config->periph_id = clock_decode_periph_id(blob, node); +	if (config->periph_id == PERIPH_ID_NONE) { +		debug("%s: Missing/invalid peripheral ID\n", __func__); +		return -FDT_ERR_NOTFOUND; +	} +	fdtdec_decode_gpio(blob, node, "nvidia,vbus-gpio", &config->vbus_gpio); +	fdtdec_decode_gpio(blob, node, "nvidia,phy-reset-gpio", +			&config->phy_reset_gpio); +	debug("enabled=%d, legacy_mode=%d, utmi=%d, ulpi=%d, periph_id=%d, " +		"vbus=%d, phy_reset=%d, dr_mode=%d\n", +		config->enabled, config->has_legacy_mode, config->utmi, +		config->ulpi, config->periph_id, config->vbus_gpio.gpio, +		config->phy_reset_gpio.gpio, config->dr_mode); + +	return 0; +} + +int board_usb_init(const void *blob) +{ +	struct fdt_usb config; +	enum clock_osc_freq freq; +	int node_list[USB_PORTS_MAX]; +	int node, count, i; + +	/* Set up the USB clocks correctly based on our oscillator frequency */ +	freq = clock_get_osc_freq(); +	config_clock(usb_pll[freq]); + +	/* count may return <0 on error */ +	count = fdtdec_find_aliases_for_id(blob, "usb", +			COMPAT_NVIDIA_TEGRA20_USB, node_list, USB_PORTS_MAX); +	for (i = 0; i < count; i++) { +		if (port_count == USB_PORTS_MAX) { +			printf("tegrausb: Cannot register more than %d ports\n", +				USB_PORTS_MAX); +			return -1; +		} + +		debug("USB %d: ", i); +		node = node_list[i]; +		if (!node) +			continue; +		if (fdt_decode_usb(blob, node, &config)) { +			debug("Cannot decode USB node %s\n", +			      fdt_get_name(blob, node, NULL)); +			return -1; +		} +		config.initialized = 0; + +		/* add new USB port to the list of available ports */ +		port[port_count++] = config; +	} + +	return 0; +} + +/** + * Start up the given port number (ports are numbered from 0 on each board). + * This returns values for the appropriate hccr and hcor addresses to use for + * USB EHCI operations. + * + * @param index	port number to start + * @param hccr		returns start address of EHCI HCCR registers + * @param hcor		returns start address of EHCI HCOR registers + * @return 0 if ok, -1 on error (generally invalid port number) + */ +int ehci_hcd_init(int index, struct ehci_hccr **hccr, struct ehci_hcor **hcor) +{ +	struct fdt_usb *config; +	struct usb_ctlr *usbctlr; + +	if (index >= port_count)  		return -1; -	*hccr = (struct ehci_hccr *)our_hccr; -	*hcor = (struct ehci_hcor *)our_hcor; +	config = &port[index]; + +	/* skip init, if the port is already initialized */ +	if (config->initialized) +		goto success; + +	if (config->utmi && init_utmi_usb_controller(config)) { +		printf("tegrausb: Cannot init port %d\n", index); +		return -1; +	} + +	if (config->ulpi && init_ulpi_usb_controller(config)) { +		printf("tegrausb: Cannot init port %d\n", index); +		return -1; +	} +	set_host_mode(config); + +	config->initialized = 1; + +success: +	usbctlr = config->reg; +	*hccr = (struct ehci_hccr *)&usbctlr->cap_length; +	*hcor = (struct ehci_hcor *)&usbctlr->usb_cmd;  	return 0;  }  /* - * Destroy the appropriate control structures corresponding - * the the EHCI host controller. + * Bring down the specified USB controller   */  int ehci_hcd_stop(int index)  { -	return tegrausb_stop_port(index); +	struct usb_ctlr *usbctlr; + +	usbctlr = port[index].reg; + +	/* Stop controller */ +	writel(0, &usbctlr->usb_cmd); +	udelay(1000); + +	/* Initiate controller reset */ +	writel(2, &usbctlr->usb_cmd); +	udelay(1000); + +	port[index].initialized = 0; + +	return 0;  } diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 1e3cd793b..d090f0a53 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -69,6 +69,7 @@ struct ehci_hcor {  #define CMD_RUN		(1 << 0)		/* start/stop HC */  	uint32_t or_usbsts;  #define STS_ASS		(1 << 15) +#define	STS_PSS		(1 << 14)  #define STS_HALT	(1 << 12)  	uint32_t or_usbintr;  #define INTR_UE         (1 << 0)                /* USB interrupt enable */ @@ -245,7 +246,10 @@ struct QH {  	 * Add dummy fill value to make the size of this struct  	 * aligned to 32 bytes  	 */ -	uint8_t fill[16]; +	union { +		uint32_t fill[4]; +		void *buffer; +	};  };  /* Low level init functions */ diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 170a358b5..e8cecca55 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -40,6 +40,7 @@ COBJS-$(CONFIG_S6E63D6) += s6e63d6.o  COBJS-$(CONFIG_LD9040) += ld9040.o  COBJS-$(CONFIG_SED156X) += sed156x.o  COBJS-$(CONFIG_VIDEO_AMBA) += amba.o +COBJS-$(CONFIG_VIDEO_BCM2835) += bcm2835.o  COBJS-$(CONFIG_VIDEO_COREBOOT) += coreboot_fb.o  COBJS-$(CONFIG_VIDEO_CT69000) += ct69000.o videomodes.o  COBJS-$(CONFIG_VIDEO_DA8XX) += da8xx-fb.o videomodes.o diff --git a/drivers/video/bcm2835.c b/drivers/video/bcm2835.c new file mode 100644 index 000000000..1e9a84ac1 --- /dev/null +++ b/drivers/video/bcm2835.c @@ -0,0 +1,127 @@ +/* + * (C) Copyright 2012 Stephen Warren + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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. + */ + +#include <common.h> +#include <lcd.h> +#include <asm/arch/mbox.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Global variables that lcd.c expects to exist */ +int lcd_line_length; +int lcd_color_fg; +int lcd_color_bg; +void *lcd_base; +void *lcd_console_address; +short console_col; +short console_row; +vidinfo_t panel_info; +char lcd_cursor_enabled; +ushort lcd_cursor_width; +ushort lcd_cursor_height; + +struct msg_query { +	struct bcm2835_mbox_hdr hdr; +	struct bcm2835_mbox_tag_physical_w_h physical_w_h; +	u32 end_tag; +}; + +struct msg_setup { +	struct bcm2835_mbox_hdr hdr; +	struct bcm2835_mbox_tag_physical_w_h physical_w_h; +	struct bcm2835_mbox_tag_virtual_w_h virtual_w_h; +	struct bcm2835_mbox_tag_depth depth; +	struct bcm2835_mbox_tag_pixel_order pixel_order; +	struct bcm2835_mbox_tag_alpha_mode alpha_mode; +	struct bcm2835_mbox_tag_virtual_offset virtual_offset; +	struct bcm2835_mbox_tag_overscan overscan; +	struct bcm2835_mbox_tag_allocate_buffer allocate_buffer; +	u32 end_tag; +}; + +void lcd_ctrl_init(void *lcdbase) +{ +	ALLOC_ALIGN_BUFFER(struct msg_query, msg_query, 1, 16); +	ALLOC_ALIGN_BUFFER(struct msg_setup, msg_setup, 1, 16); +	int ret; +	u32 w, h; + +	debug("bcm2835: Query resolution...\n"); + +	BCM2835_MBOX_INIT_HDR(msg_query); +	BCM2835_MBOX_INIT_TAG_NO_REQ(&msg_query->physical_w_h, +					GET_PHYSICAL_W_H); +	ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg_query->hdr); +	if (ret) { +		printf("bcm2835: Could not query display resolution\n"); +		/* FIXME: How to disable the LCD to prevent errors? hang()? */ +		return; +	} + +	w = msg_query->physical_w_h.body.resp.width; +	h = msg_query->physical_w_h.body.resp.height; + +	debug("bcm2835: Setting up display for %d x %d\n", w, h); + +	BCM2835_MBOX_INIT_HDR(msg_setup); +	BCM2835_MBOX_INIT_TAG(&msg_setup->physical_w_h, SET_PHYSICAL_W_H); +	msg_setup->physical_w_h.body.req.width = w; +	msg_setup->physical_w_h.body.req.height = h; +	BCM2835_MBOX_INIT_TAG(&msg_setup->virtual_w_h, SET_VIRTUAL_W_H); +	msg_setup->virtual_w_h.body.req.width = w; +	msg_setup->virtual_w_h.body.req.height = h; +	BCM2835_MBOX_INIT_TAG(&msg_setup->depth, SET_DEPTH); +	msg_setup->depth.body.req.bpp = 16; +	BCM2835_MBOX_INIT_TAG(&msg_setup->pixel_order, SET_PIXEL_ORDER); +	msg_setup->pixel_order.body.req.order = BCM2835_MBOX_PIXEL_ORDER_BGR; +	BCM2835_MBOX_INIT_TAG(&msg_setup->alpha_mode, SET_ALPHA_MODE); +	msg_setup->alpha_mode.body.req.alpha = BCM2835_MBOX_ALPHA_MODE_IGNORED; +	BCM2835_MBOX_INIT_TAG(&msg_setup->virtual_offset, SET_VIRTUAL_OFFSET); +	msg_setup->virtual_offset.body.req.x = 0; +	msg_setup->virtual_offset.body.req.y = 0; +	BCM2835_MBOX_INIT_TAG(&msg_setup->overscan, SET_OVERSCAN); +	msg_setup->overscan.body.req.top = 0; +	msg_setup->overscan.body.req.bottom = 0; +	msg_setup->overscan.body.req.left = 0; +	msg_setup->overscan.body.req.right = 0; +	BCM2835_MBOX_INIT_TAG(&msg_setup->allocate_buffer, ALLOCATE_BUFFER); +	msg_setup->allocate_buffer.body.req.alignment = 0x100; + +	ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg_setup->hdr); +	if (ret) { +		printf("bcm2835: Could not configure display\n"); +		/* FIXME: How to disable the LCD to prevent errors? hang()? */ +		return; +	} + +	w = msg_setup->physical_w_h.body.resp.width; +	h = msg_setup->physical_w_h.body.resp.height; + +	debug("bcm2835: Final resolution is %d x %d\n", w, h); + +	panel_info.vl_col = w; +	panel_info.vl_row = h; +	panel_info.vl_bpix = LCD_COLOR16; + +	gd->fb_base = msg_setup->allocate_buffer.body.resp.fb_address; +	lcd_base = (void *)gd->fb_base; +} + +void lcd_enable(void) +{ +} diff --git a/drivers/video/omap3_dss.c b/drivers/video/omap3_dss.c index b1424bfd0..6efba122e 100644 --- a/drivers/video/omap3_dss.c +++ b/drivers/video/omap3_dss.c @@ -121,7 +121,7 @@ void omap3_dss_panel_config(const struct panel_config *panel_cfg)  	if (!panel_cfg->frame_buffer)  		return; -	writel(8 << GFX_FORMAT_SHIFT | GFX_ENABLE, &dispc->gfx_attributes); +	writel(panel_cfg->gfx_format | GFX_ENABLE, &dispc->gfx_attributes);  	writel(1, &dispc->gfx_row_inc);  	writel(1, &dispc->gfx_pixel_inc);  	writel(panel_cfg->lcd_size, &dispc->gfx_size); |