diff --git a/Makefile b/Makefile index a3e733f..38dc3b0 100644 --- a/Makefile +++ b/Makefile @@ -298,7 +298,7 @@ -fno-strict-aliasing -fno-common -Os -pipe AFLAGS := -D__ASSEMBLY__ -LDFLAGS := -Map barebox.map +LDFLAGS_barebox := -Map barebox.map # Read KERNELRELEASE from include/config/kernel.release (if it exists) KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null) diff --git a/arch/arm/boards/at91sam9263ek/init.c b/arch/arm/boards/at91sam9263ek/init.c index 8448866..9a763b3 100644 --- a/arch/arm/boards/at91sam9263ek/init.c +++ b/arch/arm/boards/at91sam9263ek/init.c @@ -99,6 +99,21 @@ .phy_addr = 0, }; +#if defined(CONFIG_MCI_ATMEL) +static struct atmel_mci_platform_data __initdata ek_mci_data = { + .bus_width = 4, + .detect_pin = AT91_PIN_PE18, + .wp_pin = AT91_PIN_PE19, +}; + +static void ek_add_device_mci(void) +{ + at91_add_device_mci(1, &ek_mci_data); +} +#else +static void ek_add_device_mci(void) {} +#endif + static int at91sam9263ek_devices_init(void) { /* @@ -113,6 +128,7 @@ ek_add_device_nand(); at91_add_device_eth(&macb_pdata); register_device(&cfi_dev); + ek_add_device_mci(); #if defined(CONFIG_DRIVER_CFI) || defined(CONFIG_DRIVER_CFI_OLD) devfs_add_partition("nor0", 0x00000, 0x40000, PARTITION_FIXED, "self"); diff --git a/arch/arm/boards/at91sam9m10g45ek/init.c b/arch/arm/boards/at91sam9m10g45ek/init.c index 77d51b7..8cdcb8d 100644 --- a/arch/arm/boards/at91sam9m10g45ek/init.c +++ b/arch/arm/boards/at91sam9m10g45ek/init.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -110,11 +111,28 @@ .phy_addr = 0, }; +#if defined(CONFIG_MCI_ATMEL) +static struct atmel_mci_platform_data ek_mci_data = { + .bus_width = 4, + .host_caps = MMC_MODE_HS, + .detect_pin = AT91_PIN_PD10, +}; + +static void ek_add_device_mci(void) +{ + at91_add_device_mci(0, &ek_mci_data); +} +#else +static void ek_add_device_mci(void) {} +#endif + + static int at91sam9m10g45ek_devices_init(void) { at91_add_device_sdram(128 * 1024 * 1024); ek_add_device_nand(); at91_add_device_eth(&macb_pdata); + ek_add_device_mci(); devfs_add_partition("nand0", 0x00000, 0x80000, PARTITION_FIXED, "self_raw"); dev_add_bb_dev("self_raw", "self0"); diff --git a/arch/arm/boards/pcm049/board.c b/arch/arm/boards/pcm049/board.c index a33a877..2303a9c 100644 --- a/arch/arm/boards/pcm049/board.c +++ b/arch/arm/boards/pcm049/board.c @@ -145,6 +145,8 @@ register_device(&sram_dev); register_device(&hsmmc_dev); + gpmc_generic_init(0x10); + pcm049_network_init(); gpmc_generic_nand_devices_init(0, 8, OMAP_ECC_BCH8_CODE_HW); diff --git a/arch/arm/configs/at91sam9263ek_defconfig b/arch/arm/configs/at91sam9263ek_defconfig index 271f7b2..308f0cd 100644 --- a/arch/arm/configs/at91sam9263ek_defconfig +++ b/arch/arm/configs/at91sam9263ek_defconfig @@ -40,3 +40,8 @@ CONFIG_NAND=y CONFIG_NAND_ATMEL=y CONFIG_UBI=y +CONFIG_MCI=y +CONFIG_MCI_ATMEL=y +CONFIG_FS_FAT=y +CONFIG_FS_FAT_WRITE=y +CONFIG_FS_FAT_LFN=y diff --git a/arch/arm/configs/at91sam9m10g45ek_defconfig b/arch/arm/configs/at91sam9m10g45ek_defconfig index e1c6cef..deca884 100644 --- a/arch/arm/configs/at91sam9m10g45ek_defconfig +++ b/arch/arm/configs/at91sam9m10g45ek_defconfig @@ -53,3 +53,8 @@ CONFIG_MTD=y CONFIG_NAND=y CONFIG_UBI=y +CONFIG_MCI=y +CONFIG_MCI_WRITE=y +CONFIG_MCI_ATMEL=y +CONFIG_FS_FAT=y +CONFIG_FS_FAT_WRITE=y diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c index fc8f828..d44e280 100644 --- a/arch/arm/mach-at91/at91sam9260_devices.c +++ b/arch/arm/mach-at91/at91sam9260_devices.c @@ -10,6 +10,7 @@ * */ #include +#include #include #include #include @@ -280,3 +281,52 @@ return; } } + +#if defined(CONFIG_MCI_ATMEL) +static struct device_d mci_device = { + .id = -1, + .name = "atmel_mci", + .map_base = AT91SAM9260_BASE_MCI, + .size = SZ_16K, +}; + +/* Consider only one slot : slot 0 */ +void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) +{ + if (!data) + return; + + /* need bus_width */ + if (!data->bus_width) + return; + + /* input/irq */ + if (data->detect_pin) { + at91_set_gpio_input(data->detect_pin, 1); + at91_set_deglitch(data->detect_pin, 1); + } + + if (data->wp_pin) + at91_set_gpio_input(data->wp_pin, 1); + + /* CLK */ + at91_set_A_periph(AT91_PIN_PA8, 0); + + /* CMD */ + at91_set_A_periph(AT91_PIN_PA7, 1); + + /* DAT0, maybe DAT1..DAT3 */ + at91_set_A_periph(AT91_PIN_PA6, 1); + if (data->bus_width == 4) { + at91_set_A_periph(AT91_PIN_PA9, 1); + at91_set_A_periph(AT91_PIN_PA10, 1); + at91_set_A_periph(AT91_PIN_PA11, 1); + } + + mci_device.platform_data = data; + at91_clock_associate("mci_clk", &mci_device, "mci_clk"); + register_device(&mci_device); +} +#else +void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) {} +#endif diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c index 66bf3a8..d8b70a3 100644 --- a/arch/arm/mach-at91/at91sam9261_devices.c +++ b/arch/arm/mach-at91/at91sam9261_devices.c @@ -10,6 +10,7 @@ * */ #include +#include #include #include #include @@ -173,3 +174,52 @@ return; } } + +#if defined(CONFIG_MCI_ATMEL) +static struct device_d mci_device = { + .id = -1, + .name = "atmel_mci", + .map_base = AT91SAM9261_BASE_MCI, + .size = SZ_16K, +}; + +/* Consider only one slot : slot 0 */ +void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) +{ + if (!data) + return; + + /* need bus_width */ + if (!data->bus_width) + return; + + /* input/irq */ + if (data->detect_pin) { + at91_set_gpio_input(data->detect_pin, 1); + at91_set_deglitch(data->detect_pin, 1); + } + + if (data->wp_pin) + at91_set_gpio_input(data->wp_pin, 1); + + /* CLK */ + at91_set_B_periph(AT91_PIN_PA2, 0); + + /* CMD */ + at91_set_B_periph(AT91_PIN_PA1, 1); + + /* DAT0, maybe DAT1..DAT3 */ + at91_set_B_periph(AT91_PIN_PA0, 1); + if (data->bus_width == 4) { + at91_set_B_periph(AT91_PIN_PA4, 1); + at91_set_B_periph(AT91_PIN_PA5, 1); + at91_set_B_periph(AT91_PIN_PA6, 1); + } + + mci_device.platform_data = data; + at91_clock_associate("mci_clk", &mci_device, "mci_clk"); + register_device(&mci_device); +} +#else +void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) {} +#endif diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c index 346426c..04fb79e 100644 --- a/arch/arm/mach-at91/at91sam9263_devices.c +++ b/arch/arm/mach-at91/at91sam9263_devices.c @@ -10,6 +10,7 @@ * */ #include +#include #include #include #include @@ -213,3 +214,80 @@ } } + +#if defined(CONFIG_MCI_ATMEL) +static struct device_d mci0_device = { + .id = 0, + .name = "atmel_mci", + .map_base = AT91SAM9263_BASE_MCI0, + .size = SZ_16K, +}; + +static struct device_d mci1_device = { + .id = 1, + .name = "atmel_mci", + .map_base = AT91SAM9263_BASE_MCI1, + .size = SZ_16K, +}; + +/* Consider only one slot : slot 0 */ +void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) +{ + if (!data) + return; + + /* need bus_width */ + if (!data->bus_width) + return; + + /* input/irq */ + if (data->detect_pin) { + at91_set_gpio_input(data->detect_pin, 1); + at91_set_deglitch(data->detect_pin, 1); + } + + if (data->wp_pin) + at91_set_gpio_input(data->wp_pin, 1); + + if (mmc_id == 0) { /* MCI0 */ + /* CLK */ + at91_set_A_periph(AT91_PIN_PA12, 0); + + /* CMD */ + at91_set_A_periph(AT91_PIN_PA1, 1); + + /* DAT0, maybe DAT1..DAT3 and maybe DAT4..DAT7 */ + at91_set_A_periph(AT91_PIN_PA0, 1); + if (data->bus_width == 4) { + at91_set_A_periph(AT91_PIN_PA3, 1); + at91_set_A_periph(AT91_PIN_PA4, 1); + at91_set_A_periph(AT91_PIN_PA5, 1); + } + + mci0_device.platform_data = data; + at91_clock_associate("mci0_clk", &mci0_device, "mci_clk"); + register_device(&mci0_device); + + } else { /* MCI1 */ + /* CLK */ + at91_set_A_periph(AT91_PIN_PA6, 0); + + /* CMD */ + at91_set_A_periph(AT91_PIN_PA7, 1); + + /* DAT0, maybe DAT1..DAT3 */ + at91_set_A_periph(AT91_PIN_PA8, 1); + if (data->bus_width == 4) { + at91_set_A_periph(AT91_PIN_PA9, 1); + at91_set_A_periph(AT91_PIN_PA10, 1); + at91_set_A_periph(AT91_PIN_PA11, 1); + } + + mci1_device.platform_data = data; + at91_clock_associate("mci1_clk", &mci1_device, "mci_clk"); + register_device(&mci1_device); + } +} +#else +void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) {} +#endif diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c index ddb005a..a474bd7 100644 --- a/arch/arm/mach-at91/at91sam9g45_devices.c +++ b/arch/arm/mach-at91/at91sam9g45_devices.c @@ -10,6 +10,7 @@ * */ #include +#include #include #include #include @@ -240,3 +241,93 @@ } } + +#if defined(CONFIG_MCI_ATMEL) +static struct device_d mci0_device = { + .id = 0, + .name = "atmel_mci", + .map_base = AT91SAM9G45_BASE_MCI0, + .size = SZ_16K, +}; + +static struct device_d mci1_device = { + .id = 1, + .name = "atmel_mci", + .map_base = AT91SAM9G45_BASE_MCI1, + .size = SZ_16K, +}; + +/* Consider only one slot : slot 0 */ +void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) +{ + if (!data) + return; + + /* need bus_width */ + if (!data->bus_width) + return; + + /* input/irq */ + if (data->detect_pin) { + at91_set_gpio_input(data->detect_pin, 1); + at91_set_deglitch(data->detect_pin, 1); + } + + if (data->wp_pin) + at91_set_gpio_input(data->wp_pin, 1); + + if (mmc_id == 0) { /* MCI0 */ + /* CLK */ + at91_set_A_periph(AT91_PIN_PA0, 0); + + /* CMD */ + at91_set_A_periph(AT91_PIN_PA1, 1); + + /* DAT0, maybe DAT1..DAT3 and maybe DAT4..DAT7 */ + at91_set_A_periph(AT91_PIN_PA2, 1); + if (data->bus_width >= 4) { + at91_set_A_periph(AT91_PIN_PA3, 1); + at91_set_A_periph(AT91_PIN_PA4, 1); + at91_set_A_periph(AT91_PIN_PA5, 1); + if (data->bus_width == 8) { + at91_set_A_periph(AT91_PIN_PA6, 1); + at91_set_A_periph(AT91_PIN_PA7, 1); + at91_set_A_periph(AT91_PIN_PA8, 1); + at91_set_A_periph(AT91_PIN_PA9, 1); + } + } + + mci0_device.platform_data = data; + at91_clock_associate("mci0_clk", &mci0_device, "mci_clk"); + register_device(&mci0_device); + + } else { /* MCI1 */ + /* CLK */ + at91_set_A_periph(AT91_PIN_PA31, 0); + + /* CMD */ + at91_set_A_periph(AT91_PIN_PA22, 1); + + /* DAT0, maybe DAT1..DAT3 and maybe DAT4..DAT7 */ + at91_set_A_periph(AT91_PIN_PA23, 1); + if (data->bus_width >= 4) { + at91_set_A_periph(AT91_PIN_PA24, 1); + at91_set_A_periph(AT91_PIN_PA25, 1); + at91_set_A_periph(AT91_PIN_PA26, 1); + if (data->bus_width == 8) { + at91_set_A_periph(AT91_PIN_PA27, 1); + at91_set_A_periph(AT91_PIN_PA28, 1); + at91_set_A_periph(AT91_PIN_PA29, 1); + at91_set_A_periph(AT91_PIN_PA30, 1); + } + } + + mci1_device.platform_data = data; + at91_clock_associate("mci1_clk", &mci1_device, "mci_clk"); + register_device(&mci1_device); + } +} +#else +void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data) {} +#endif + diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index 1ab05ad..89caebb 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -21,6 +21,7 @@ #ifndef __ASM_ARCH_BOARD_H #define __ASM_ARCH_BOARD_H +#include #include void atmel_nand_load_image(void *dest, int size, int pagesize, int blocksize); @@ -47,6 +48,7 @@ struct at91_ether_platform_data { unsigned int flags; int phy_addr; + int (*get_ethaddr)(struct eth_device*, unsigned char *adr); }; void at91_add_device_eth(struct at91_ether_platform_data *data); @@ -63,4 +65,14 @@ #define ATMEL_UART_RI 0x20 void at91_register_uart(unsigned id, unsigned pins); + +/* Multimedia Card Interface */ +struct atmel_mci_platform_data { + unsigned bus_width; + unsigned host_caps; /* MCI_MODE_* from mci.h */ + unsigned detect_pin; + unsigned wp_pin; +}; + +void at91_add_device_mci(short mmc_id, struct atmel_mci_platform_data *data); #endif diff --git a/arch/arm/mach-versatile/include/mach/debug_ll.h b/arch/arm/mach-versatile/include/mach/debug_ll.h index 514fcfb..1f6162b 100644 --- a/arch/arm/mach-versatile/include/mach/debug_ll.h +++ b/arch/arm/mach-versatile/include/mach/debug_ll.h @@ -4,7 +4,7 @@ * * barebox 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 3 of the License, or + * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * barebox is distributed in the hope that it will be useful, diff --git a/arch/nios2/cpu/exceptions.S b/arch/nios2/cpu/exceptions.S index a949372..8e024ce 100644 --- a/arch/nios2/cpu/exceptions.S +++ b/arch/nios2/cpu/exceptions.S @@ -122,7 +122,7 @@ mov r4, sp /* ptr to regs */ callr r3 - /* Restore regsisters and return from exception*/ + /* Restore registers and return from exception */ _exception_return: ldw r1, 4(sp) ldw r2, 8(sp) diff --git a/arch/x86/include/asm/posix_types.h b/arch/x86/include/asm/posix_types.h index 9278681..00c9256 100644 --- a/arch/x86/include/asm/posix_types.h +++ b/arch/x86/include/asm/posix_types.h @@ -20,7 +20,7 @@ * @file * @brief x86 posix types * - * Minimal set to make all the other header files copied from the Linxu kernel happy + * Minimal set to make all the other header files copied from the Linux kernel happy */ #ifndef _ASM_X86_POSIX_TYPES_H diff --git a/commands/bootm.c b/commands/bootm.c index 34164bc..e5ffacb 100644 --- a/commands/bootm.c +++ b/commands/bootm.c @@ -282,7 +282,6 @@ static int do_bootm(struct command *cmdtp, int argc, char *argv[]) { - ulong iflag; int opt; image_header_t *os_header; struct image_handle *os_handle, *initrd_handle = NULL; @@ -344,7 +343,7 @@ * recover from any failures any more... */ - iflag = disable_interrupts(); + disable_interrupts(); puts ("OK\n"); diff --git a/common/clock.c b/common/clock.c index 15df0ab..79c06c8 100644 --- a/common/clock.c +++ b/common/clock.c @@ -57,6 +57,61 @@ EXPORT_SYMBOL(get_time_ns); /** + * clocks_calc_mult_shift - calculate mult/shift factors for scaled math of clocks + * @mult: pointer to mult variable + * @shift: pointer to shift variable + * @from: frequency to convert from + * @to: frequency to convert to + * @maxsec: guaranteed runtime conversion range in seconds + * + * The function evaluates the shift/mult pair for the scaled math + * operations of clocksources and clockevents. + * + * @to and @from are frequency values in HZ. For clock sources @to is + * NSEC_PER_SEC == 1GHz and @from is the counter frequency. For clock + * event @to is the counter frequency and @from is NSEC_PER_SEC. + * + * The @maxsec conversion range argument controls the time frame in + * seconds which must be covered by the runtime conversion with the + * calculated mult and shift factors. This guarantees that no 64bit + * overflow happens when the input value of the conversion is + * multiplied with the calculated mult factor. Larger ranges may + * reduce the conversion accuracy by chosing smaller mult and shift + * factors. + */ + +void clocks_calc_mult_shift(uint32_t *mult, uint32_t *shift, uint32_t from, uint32_t to, uint32_t maxsec) +{ + uint64_t tmp; + uint32_t sft, sftacc = 32; + + /* + * Calculate the shift factor which is limiting the conversion + * range: + */ + tmp = ((uint64_t)maxsec * from) >> 32; + while (tmp) { + tmp >>=1; + sftacc--; + } + + /* + * Find the conversion shift/mult pair which has the best + * accuracy and fits the maxsec conversion range: + */ + for (sft = 32; sft > 0; sft--) { + tmp = (uint64_t) to << sft; + tmp += from / 2; + do_div(tmp, from); + if ((tmp >> sftacc) == 0) + break; + } + *mult = tmp; + *shift = sft; +} + + +/** * clocksource_hz2mult - calculates mult from hz and shift * @hz: Clocksource frequency in Hz * @shift_constant: Clocksource shift factor diff --git a/defaultenv/bin/_update_help b/defaultenv/bin/_update_help index 5e3ca45..98096da 100644 --- a/defaultenv/bin/_update_help +++ b/defaultenv/bin/_update_help @@ -1,6 +1,6 @@ #!/bin/sh -echo "usage: update -t -d [-m tftp|xmodem] [-f imagename] -c" +echo "usage: update -t -d [-m tftp|xmodem] [-f imagename] -c" echo "update tools." echo "" echo "options" @@ -11,3 +11,4 @@ echo "type update -t rootfs -d [-m tftp|xmodem] [-f imagename] to update rootfs into flash" echo "type update -t barebox -d [-m tftp|xmodem] [-f imagename] to update barebox into flash" echo "type update -t bareboxenv -d [-m tftp|xmodem] [-f imagename] to update bareboxenv into flash" +echo "type update -t xload -d [-m tftp|xmodem] [-f imagename] to update xload into flash" diff --git a/defaultenv/bin/update b/defaultenv/bin/update index 7c37c36..55ac10b 100644 --- a/defaultenv/bin/update +++ b/defaultenv/bin/update @@ -40,6 +40,8 @@ if [ x${image} = x ]; then image=bareboxenv.bin fi +elif [ x${type} = xxload ]; then + image=$xloadimage else . /env/bin/_update_help exit 1 diff --git a/drivers/Kconfig b/drivers/Kconfig index 86d8fb5..c4e1517 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -13,5 +13,6 @@ source "drivers/clk/Kconfig" source "drivers/mfd/Kconfig" source "drivers/led/Kconfig" +source "drivers/eeprom/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index b1b402f..92b22bd 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -11,3 +11,4 @@ obj-y += clk/ obj-y += mfd/ obj-$(CONFIG_LED) += led/ +obj-y += eeprom/ diff --git a/drivers/eeprom/Kconfig b/drivers/eeprom/Kconfig new file mode 100644 index 0000000..a0b5489 --- /dev/null +++ b/drivers/eeprom/Kconfig @@ -0,0 +1,11 @@ +menu "EEPROM support" + +config EEPROM_AT25 + tristate "SPI EEPROMs from most vendors" + depends on SPI + help + Enable this driver to get read/write support to most SPI EEPROMs, + after you configure the board init code to know about each eeprom + on your target board. + +endmenu diff --git a/drivers/eeprom/Makefile b/drivers/eeprom/Makefile new file mode 100644 index 0000000..e323bd0 --- /dev/null +++ b/drivers/eeprom/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_EEPROM_AT25) += at25.o diff --git a/drivers/eeprom/at25.c b/drivers/eeprom/at25.c new file mode 100644 index 0000000..8a979d5 --- /dev/null +++ b/drivers/eeprom/at25.c @@ -0,0 +1,319 @@ +/* + * at25.c -- support most SPI EEPROMs, such as Atmel AT25 models + * + * Copyright (C) 2011 Hubert Feurstein + * + * based on linux driver by: + * Copyright (C) 2006 David Brownell + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * NOTE: this is an *EEPROM* driver. The vagaries of product naming + * mean that some AT25 products are EEPROMs, and others are FLASH. + * Handle FLASH chips with the drivers/mtd/devices/m25p80.c driver, + * not this one! + */ + +struct at25_data { + struct cdev cdev; + struct spi_device *spi; + struct spi_eeprom chip; + unsigned addrlen; +}; + +#define to_at25_data(cdev) ((struct at25_data *)(cdev)->priv) + +#define AT25_WREN 0x06 /* latch the write enable */ +#define AT25_WRDI 0x04 /* reset the write enable */ +#define AT25_RDSR 0x05 /* read status register */ +#define AT25_WRSR 0x01 /* write status register */ +#define AT25_READ 0x03 /* read byte(s) */ +#define AT25_WRITE 0x02 /* write byte(s)/sector */ + +#define AT25_SR_nRDY 0x01 /* nRDY = write-in-progress */ +#define AT25_SR_WEN 0x02 /* write enable (latched) */ +#define AT25_SR_BP0 0x04 /* BP for software writeprotect */ +#define AT25_SR_BP1 0x08 +#define AT25_SR_WPEN 0x80 /* writeprotect enable */ + + +#define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */ + +/* Specs often allow 5 msec for a page write, sometimes 20 msec; + * it's important to recover from write timeouts. + */ +#define EE_TIMEOUT (25 * MSECOND) +#define DRIVERNAME "at25x" + +/*-------------------------------------------------------------------------*/ + +#define io_limit PAGE_SIZE /* bytes */ + +static ssize_t at25_ee_read(struct cdev *cdev, + void *buf, + size_t count, + ulong offset, + ulong flags) +{ + u8 command[EE_MAXADDRLEN + 1]; + u8 *cp; + ssize_t status; + struct spi_transfer t[2]; + struct spi_message m; + struct at25_data *at25 = to_at25_data(cdev); + + if (unlikely(offset >= at25->chip.size)) + return 0; + if ((offset + count) > at25->chip.size) + count = at25->chip.size - offset; + if (unlikely(!count)) + return count; + + cp = command; + *cp++ = AT25_READ; + + /* 8/16/24-bit address is written MSB first */ + switch (at25->addrlen) { + default: /* case 3 */ + *cp++ = offset >> 16; + case 2: + *cp++ = offset >> 8; + case 1: + case 0: /* can't happen: for better codegen */ + *cp++ = offset >> 0; + } + + spi_message_init(&m); + memset(t, 0, sizeof t); + + t[0].tx_buf = command; + t[0].len = at25->addrlen + 1; + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = buf; + t[1].len = count; + spi_message_add_tail(&t[1], &m); + + /* Read it all at once. + * + * REVISIT that's potentially a problem with large chips, if + * other devices on the bus need to be accessed regularly or + * this chip is clocked very slowly + */ + status = spi_sync(at25->spi, &m); + dev_dbg(at25->cdev.dev, + "read %d bytes at %lu --> %d\n", + count, offset, (int) status); + + return status ? status : count; +} + +static ssize_t at25_ee_write(struct cdev *cdev, + const void *buf, + size_t count, + ulong off, + ulong flags) +{ + ssize_t status = 0; + unsigned written = 0; + unsigned buf_size; + u8 *bounce; + struct at25_data *at25 = to_at25_data(cdev); + + if (unlikely(off >= at25->chip.size)) + return -EFBIG; + if ((off + count) > at25->chip.size) + count = at25->chip.size - off; + if (unlikely(!count)) + return count; + + /* Temp buffer starts with command and address */ + buf_size = at25->chip.page_size; + if (buf_size > io_limit) + buf_size = io_limit; + bounce = xmalloc(buf_size + at25->addrlen + 1); + + /* For write, rollover is within the page ... so we write at + * most one page, then manually roll over to the next page. + */ + bounce[0] = AT25_WRITE; + + do { + uint64_t start_time; + unsigned segment; + unsigned offset = (unsigned) off; + u8 *cp = bounce + 1; + int sr; + + *cp = AT25_WREN; + status = spi_write(at25->spi, cp, 1); + if (status < 0) { + dev_dbg(at25->cdev.dev, "WREN --> %d\n", + (int) status); + break; + } + + /* 8/16/24-bit address is written MSB first */ + switch (at25->addrlen) { + default: /* case 3 */ + *cp++ = offset >> 16; + case 2: + *cp++ = offset >> 8; + case 1: + case 0: /* can't happen: for better codegen */ + *cp++ = offset >> 0; + } + + /* Write as much of a page as we can */ + segment = buf_size - (offset % buf_size); + if (segment > count) + segment = count; + memcpy(cp, buf, segment); + status = spi_write(at25->spi, bounce, + segment + at25->addrlen + 1); + dev_dbg(at25->cdev.dev, + "write %u bytes at %u --> %d\n", + segment, offset, (int) status); + if (status < 0) + break; + + /* REVISIT this should detect (or prevent) failed writes + * to readonly sections of the EEPROM... + */ + + /* Wait for non-busy status */ + start_time = get_time_ns(); + + do { + sr = spi_w8r8(at25->spi, AT25_RDSR); + if (sr < 0 || (sr & AT25_SR_nRDY)) { + dev_dbg(at25->cdev.dev, + "rdsr --> %d (%02x)\n", sr, sr); + continue; + } + if (!(sr & AT25_SR_nRDY)) + break; + } while (!is_timeout(start_time, EE_TIMEOUT)); + + if ((sr < 0) || (sr & AT25_SR_nRDY)) { + dev_err(at25->cdev.dev, + "write %d bytes offset %d, " + "timeout after %u msecs\n", + segment, offset, + (unsigned int)(get_time_ns() - start_time) / + 1000000); + status = -ETIMEDOUT; + break; + } + + off += segment; + buf += segment; + count -= segment; + written += segment; + + } while (count > 0); + + free(bounce); + return written ? written : status; +} + +static off_t at25_ee_lseek(struct cdev *cdev, off_t off) +{ + return off; +} + +static struct file_operations at25_fops = { + .read = at25_ee_read, + .write = at25_ee_write, + .lseek = at25_ee_lseek, +}; + +static int at25_probe(struct device_d *dev) +{ + int err, sr; + int addrlen; + struct at25_data *at25 = NULL; + const struct spi_eeprom *chip; + + /* Chip description */ + chip = dev->platform_data; + if (!chip) { + dev_dbg(dev, "no chip description\n"); + err = -ENODEV; + goto fail; + } + + /* For now we only support 8/16/24 bit addressing */ + if (chip->flags & EE_ADDR1) { + addrlen = 1; + } else if (chip->flags & EE_ADDR2) { + addrlen = 2; + } else if (chip->flags & EE_ADDR3) { + addrlen = 3; + } else { + dev_dbg(dev, "unsupported address type\n"); + err = -EINVAL; + goto fail; + } + + at25 = xzalloc(sizeof(*at25)); + at25->chip = *chip; + at25->addrlen = addrlen; + at25->spi = dev->type_data; + at25->spi->mode = SPI_MODE_0; + at25->spi->bits_per_word = 8; + at25->cdev.ops = &at25_fops; + at25->cdev.size = chip->size; + at25->cdev.dev = dev; + at25->cdev.name = at25->chip.name[0] ? at25->chip.name : DRIVERNAME; + at25->cdev.priv = at25; + + /* Ping the chip ... the status register is pretty portable, + * unlike probing manufacturer IDs. We do expect that system + * firmware didn't write it in the past few milliseconds! + */ + sr = spi_w8r8(at25->spi, AT25_RDSR); + if (sr < 0 || sr & AT25_SR_nRDY) { + dev_dbg(dev, "rdsr --> %d (%02x)\n", sr, sr); + err = -ENXIO; + goto fail; + } + + dev_dbg(dev, "%s probed\n", at25->cdev.name); + devfs_create(&at25->cdev); + return 0; + +fail: + if (at25) + free(at25); + + return err; +} + +static struct driver_d at25_driver = { + .name = DRIVERNAME, + .probe = at25_probe, +}; + +static int at25_init(void) +{ + register_driver(&at25_driver); + return 0; +} + +device_initcall(at25_init); diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig index 5d8adbd..7b71b99 100644 --- a/drivers/mci/Kconfig +++ b/drivers/mci/Kconfig @@ -73,4 +73,11 @@ Enable this entry to add support to read and write SD cards on a OMAP4 based system. +config MCI_ATMEL + bool "ATMEL (AT91)" + depends on ARCH_AT91 + help + Enable this entry to add support to read and write SD cards on a + Atmel AT91. + endif diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile index 2bb9a93..4fc0046 100644 --- a/drivers/mci/Makefile +++ b/drivers/mci/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_MCI_IMX) += imx.o obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o +obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o diff --git a/drivers/mci/at91_mci.h b/drivers/mci/at91_mci.h new file mode 100644 index 0000000..4025aeb --- /dev/null +++ b/drivers/mci/at91_mci.h @@ -0,0 +1,121 @@ +/* + * [origin: Linux kernel arch/arm/mach-at91/include/mach/at91_mci.h] + * + * Copyright (C) 2005 Ivan Kokshaysky + * Copyright (C) SAN People + * + * MultiMedia Card Interface (MCI) registers. + * Based on AT91RM9200 datasheet revision F. + * + * 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. + */ + +#ifndef AT91_MCI_H +#define AT91_MCI_H + +#define AT91_MCI_CR 0x00 /* Control Register */ +#define AT91_MCI_MCIEN (1 << 0) /* Multi-Media Interface Enable */ +#define AT91_MCI_MCIDIS (1 << 1) /* Multi-Media Interface Disable */ +#define AT91_MCI_PWSEN (1 << 2) /* Power Save Mode Enable */ +#define AT91_MCI_PWSDIS (1 << 3) /* Power Save Mode Disable */ +#define AT91_MCI_SWRST (1 << 7) /* Software Reset */ + +#define AT91_MCI_MR 0x04 /* Mode Register */ +#define AT91_MCI_CLKDIV (0xff << 0) /* Clock Divider */ +#define AT91_MCI_PWSDIV (7 << 8) /* Power Saving Divider */ +#define AT91_MCI_RDPROOF (1 << 11) /* Read Proof Enable [SAM926[03] only] */ +#define AT91_MCI_WRPROOF (1 << 12) /* Write Proof Enable [SAM926[03] only] */ +#define AT91_MCI_PDCFBYTE (1 << 13) /* PDC Force Byte Transfer [SAM926[03] only] */ +#define AT91_MCI_PDCPADV (1 << 14) /* PDC Padding Value */ +#define AT91_MCI_PDCMODE (1 << 15) /* PDC-orientated Mode */ +#define AT91_MCI_BLKLEN (0xfff << 18) /* Data Block Length */ + +#define AT91_MCI_DTOR 0x08 /* Data Timeout Register */ +#define AT91_MCI_DTOCYC (0xf << 0) /* Data Timeout Cycle Number */ +#define AT91_MCI_DTOMUL (7 << 4) /* Data Timeout Multiplier */ +#define AT91_MCI_DTOMUL_1 (0 << 4) +#define AT91_MCI_DTOMUL_16 (1 << 4) +#define AT91_MCI_DTOMUL_128 (2 << 4) +#define AT91_MCI_DTOMUL_256 (3 << 4) +#define AT91_MCI_DTOMUL_1K (4 << 4) +#define AT91_MCI_DTOMUL_4K (5 << 4) +#define AT91_MCI_DTOMUL_64K (6 << 4) +#define AT91_MCI_DTOMUL_1M (7 << 4) + +#define AT91_MCI_SDCR 0x0c /* SD Card Register */ +#define AT91_MCI_SDCSEL (3 << 0) /* SD Card Selector */ +#define AT91_MCI_SDCBUS (3 << 6) /* 1-bit, 4-bit, or 8-bit bus */ +#define AT91_MCI_SDCBUS_1BIT (0 << 6) /* 1-bit bus */ +#define AT91_MCI_SDCBUS_4BIT (2 << 6) /* 4-bit bus */ +#define AT91_MCI_SDCBUS_8BIT (3 << 6) /* 8-bit bus */ + +#define AT91_MCI_ARGR 0x10 /* Argument Register */ + +#define AT91_MCI_CMDR 0x14 /* Command Register */ +#define AT91_MCI_CMDNB (0x3f << 0) /* Command Number */ +#define AT91_MCI_RSPTYP (3 << 6) /* Response Type */ +#define AT91_MCI_RSPTYP_NONE (0 << 6) +#define AT91_MCI_RSPTYP_48 (1 << 6) +#define AT91_MCI_RSPTYP_136 (2 << 6) +#define AT91_MCI_RSPTYP_R1B (3 << 6) +#define AT91_MCI_SPCMD (7 << 8) /* Special Command */ +#define AT91_MCI_SPCMD_NONE (0 << 8) +#define AT91_MCI_SPCMD_INIT (1 << 8) +#define AT91_MCI_SPCMD_SYNC (2 << 8) +#define AT91_MCI_SPCMD_ICMD (4 << 8) +#define AT91_MCI_SPCMD_IRESP (5 << 8) +#define AT91_MCI_OPDCMD (1 << 11) /* Open Drain Command */ +#define AT91_MCI_MAXLAT (1 << 12) /* Max Latency for Command to Response */ +#define AT91_MCI_TRCMD (3 << 16) /* Transfer Command */ +#define AT91_MCI_TRCMD_NONE (0 << 16) +#define AT91_MCI_TRCMD_START (1 << 16) +#define AT91_MCI_TRCMD_STOP (2 << 16) +#define AT91_MCI_TRDIR (1 << 18) /* Transfer Direction */ +#define AT91_MCI_TRDIR_RX (1 << 18) /* Read Transfer Direction */ +#define AT91_MCI_TRDIR_TX (0 << 18) /* Write Transfer Direction */ +#define AT91_MCI_TRTYP (3 << 19) /* Transfer Type */ +#define AT91_MCI_TRTYP_BLOCK (0 << 19) +#define AT91_MCI_TRTYP_MULTIPLE (1 << 19) +#define AT91_MCI_TRTYP_STREAM (2 << 19) +#define AT91_MCI_TRTYP_SDIO_BYTE (4 << 19) +#define AT91_MCI_TRTYP_SDIO_BLOCK (5 << 19) + +#define AT91_MCI_BLKR 0x18 /* Block Register */ +#define AT91_MCI_BLKR_BCNT(n) ((0xffff & (n)) << 0) /* Block count */ +#define AT91_MCI_BLKR_BLKLEN(n) ((0xffff & (n)) << 16) /* Block length */ + +#define AT91_MCI_RSPR(n) (0x20 + ((n) * 4)) /* Response Registers 0-3 */ +#define AT91_MCI_RDR 0x30 /* Receive Data Register */ +#define AT91_MCI_TDR 0x34 /* Transmit Data Register */ + +#define AT91_MCI_SR 0x40 /* Status Register */ +#define AT91_MCI_CMDRDY (1 << 0) /* Command Ready */ +#define AT91_MCI_RXRDY (1 << 1) /* Receiver Ready */ +#define AT91_MCI_TXRDY (1 << 2) /* Transmit Ready */ +#define AT91_MCI_BLKE (1 << 3) /* Data Block Ended */ +#define AT91_MCI_DTIP (1 << 4) /* Data Transfer in Progress */ +#define AT91_MCI_NOTBUSY (1 << 5) /* Data Not Busy */ +#define AT91_MCI_ENDRX (1 << 6) /* End of RX Buffer */ +#define AT91_MCI_ENDTX (1 << 7) /* End fo TX Buffer */ +#define AT91_MCI_SDIOIRQA (1 << 8) /* SDIO Interrupt for Slot A */ +#define AT91_MCI_SDIOIRQB (1 << 9) /* SDIO Interrupt for Slot B */ +#define AT91_MCI_RXBUFF (1 << 14) /* RX Buffer Full */ +#define AT91_MCI_TXBUFE (1 << 15) /* TX Buffer Empty */ +#define AT91_MCI_RINDE (1 << 16) /* Response Index Error */ +#define AT91_MCI_RDIRE (1 << 17) /* Response Direction Error */ +#define AT91_MCI_RCRCE (1 << 18) /* Response CRC Error */ +#define AT91_MCI_RENDE (1 << 19) /* Response End Bit Error */ +#define AT91_MCI_RTOE (1 << 20) /* Response Time-out Error */ +#define AT91_MCI_DCRCE (1 << 21) /* Data CRC Error */ +#define AT91_MCI_DTOE (1 << 22) /* Data Time-out Error */ +#define AT91_MCI_OVRE (1 << 30) /* Overrun */ +#define AT91_MCI_UNRE (1 << 31) /* Underrun */ + +#define AT91_MCI_IER 0x44 /* Interrupt Enable Register */ +#define AT91_MCI_IDR 0x48 /* Interrupt Disable Register */ +#define AT91_MCI_IMR 0x4c /* Interrupt Mask Register */ + +#endif diff --git a/drivers/mci/atmel_mci.c b/drivers/mci/atmel_mci.c new file mode 100644 index 0000000..d8bcf81 --- /dev/null +++ b/drivers/mci/atmel_mci.c @@ -0,0 +1,494 @@ +/* + * Atmel AT91 MCI driver + * + * Copyright (C) 2011 Hubert Feurstein + * + * based on imx.c by: + * Copyright (C) 2009 Ilya Yanok, + * Copyright (C) 2008 Sascha Hauer, Pengutronix + * Copyright (C) 2006 Pavel Pisa, PiKRON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "at91_mci.h" + +struct atmel_mci_host { + struct mci_host mci; + void __iomem *base; + struct device_d *hw_dev; + struct clk *clk; + + u32 datasize; + struct mci_cmd *cmd; + struct mci_data *data; +}; + +#define to_mci_host(mci) container_of(mci, struct atmel_mci_host, mci) + +#define STATUS_ERROR_MASK (AT91_MCI_RINDE \ + | AT91_MCI_RDIRE \ + | AT91_MCI_RCRCE \ + | AT91_MCI_RENDE \ + | AT91_MCI_RTOE \ + | AT91_MCI_DCRCE \ + | AT91_MCI_DTOE \ + | AT91_MCI_OVRE \ + | AT91_MCI_UNRE) + +static inline u32 atmel_mci_readl(struct atmel_mci_host *host, u32 offset) +{ + return readl(host->base + offset); +} + +static inline void atmel_mci_writel(struct atmel_mci_host *host, u32 offset, + u32 value) +{ + writel(value, host->base + offset); +} + +static void atmel_mci_reset(struct atmel_mci_host *host) +{ + atmel_mci_writel(host, AT91_MCI_CR, AT91_MCI_SWRST | AT91_MCI_MCIDIS); + atmel_mci_writel(host, AT91_MCI_DTOR, 0x7f); + atmel_mci_writel(host, AT91_MCI_IDR, ~0UL); +} + +static void atmel_set_clk_rate(struct atmel_mci_host *host, + unsigned int clk_ios) +{ + unsigned int divider; + unsigned int clk_in = clk_get_rate(host->clk); + + if (clk_ios > 0) { + divider = (clk_in / clk_ios) / 2; + if (divider > 0) + divider -= 1; + } + + if (clk_ios == 0 || divider > 255) + divider = 255; + + dev_dbg(host->hw_dev, "atmel_set_clk_rate: clkIn=%d clkIos=%d divider=%d\n", + clk_in, clk_ios, divider); + + atmel_mci_writel(host, AT91_MCI_MR, (AT91_MCI_CLKDIV & divider) + | AT91_MCI_RDPROOF | AT91_MCI_WRPROOF); +} + +static int atmel_poll_status(struct atmel_mci_host *host, u32 mask) +{ + u32 stat; + uint64_t start = get_time_ns(); + + do { + stat = atmel_mci_readl(host, AT91_MCI_SR); + if (stat & STATUS_ERROR_MASK) + return stat; + if (is_timeout(start, SECOND)) { + dev_err(host->hw_dev, "timeout\n"); + return AT91_MCI_RTOE | stat; + } + if (stat & mask) + return 0; + } while (1); +} + +static int atmel_pull(struct atmel_mci_host *host, void *_buf, int bytes) +{ + unsigned int stat; + u32 *buf = _buf; + + while (bytes > 3) { + stat = atmel_poll_status(host, AT91_MCI_RXRDY); + if (stat) + return stat; + + *buf++ = atmel_mci_readl(host, AT91_MCI_RDR); + bytes -= 4; + } + + if (WARN_ON(bytes)) + return -EIO; + + return 0; +} + +#ifdef CONFIG_MCI_WRITE +static int atmel_push(struct atmel_mci_host *host, const void *_buf, int bytes) +{ + unsigned int stat; + const u32 *buf = _buf; + + while (bytes > 3) { + stat = atmel_poll_status(host, AT91_MCI_TXRDY); + if (stat) + return stat; + + atmel_mci_writel(host, AT91_MCI_TDR, *buf++); + bytes -= 4; + } + + stat = atmel_poll_status(host, AT91_MCI_TXRDY); + if (stat) + return stat; + + if (WARN_ON(bytes)) + return -EIO; + + return 0; +} +#endif /* CONFIG_MCI_WRITE */ + +static int atmel_transfer_data(struct atmel_mci_host *host) +{ + struct mci_data *data = host->data; + int stat; + unsigned long length; + + length = data->blocks * data->blocksize; + host->datasize = 0; + + if (data->flags & MMC_DATA_READ) { + stat = atmel_pull(host, data->dest, length); + if (stat) + return stat; + + stat = atmel_poll_status(host, AT91_MCI_NOTBUSY); + if (stat) + return stat; + + host->datasize += length; + } else { +#ifdef CONFIG_MCI_WRITE + stat = atmel_push(host, (const void *)(data->src), length); + if (stat) + return stat; + + host->datasize += length; + stat = atmel_poll_status(host, AT91_MCI_NOTBUSY); + if (stat) + return stat; +#endif /* CONFIG_MCI_WRITE */ + } + return 0; +} + +static void atmel_finish_request(struct atmel_mci_host *host) +{ + host->cmd = NULL; + host->data = NULL; +} + +static int atmel_finish_data(struct atmel_mci_host *host, unsigned int stat) +{ + int data_error = 0; + + if (stat & STATUS_ERROR_MASK) { + dev_err(host->hw_dev, "request failed (status=0x%08x)\n", stat); + if (stat & AT91_MCI_DCRCE) + data_error = -EILSEQ; + else if (stat & (AT91_MCI_RTOE | AT91_MCI_DTOE)) + data_error = -ETIMEDOUT; + else + data_error = -EIO; + } + + host->data = NULL; + + return data_error; +} + +static void atmel_setup_data(struct atmel_mci_host *host, struct mci_data *data) +{ + unsigned int nob = data->blocks; + unsigned int blksz = data->blocksize; + unsigned int datasize = nob * blksz; + + BUG_ON(data->blocksize & 3); + BUG_ON(nob == 0); + + host->data = data; + + dev_dbg(host->hw_dev, "atmel_setup_data: nob=%d blksz=%d\n", + nob, blksz); + + atmel_mci_writel(host, AT91_MCI_BLKR, AT91_MCI_BLKR_BCNT(nob) + | AT91_MCI_BLKR_BLKLEN(blksz)); + + host->datasize = datasize; +} + +static int atmel_read_response(struct atmel_mci_host *host, unsigned int stat) +{ + struct mci_cmd *cmd = host->cmd; + int i; + u32 *resp = (u32 *)cmd->response; + + if (!cmd) + return 0; + + if (stat & (AT91_MCI_RTOE | AT91_MCI_DTOE)) { + dev_err(host->hw_dev, "command/data timeout\n"); + return -ETIMEDOUT; + } else if ((stat & AT91_MCI_RCRCE) && (cmd->resp_type & MMC_RSP_CRC)) { + dev_err(host->hw_dev, "cmd crc error\n"); + return -EILSEQ; + } + + if (cmd->resp_type & MMC_RSP_PRESENT) { + if (cmd->resp_type & MMC_RSP_136) { + for (i = 0; i < 4; i++) + resp[i] = atmel_mci_readl(host, AT91_MCI_RSPR(0)); + } else { + resp[0] = atmel_mci_readl(host, AT91_MCI_RSPR(0)); + } + } + + return 0; +} + +static int atmel_cmd_done(struct atmel_mci_host *host, unsigned int stat) +{ + int datastat; + int ret; + + ret = atmel_read_response(host, stat); + + if (ret) { + atmel_finish_request(host); + return ret; + } + + if (!host->data) { + atmel_finish_request(host); + return 0; + } + + datastat = atmel_transfer_data(host); + ret = atmel_finish_data(host, datastat); + atmel_finish_request(host); + return ret; +} + +static int atmel_start_cmd(struct atmel_mci_host *host, struct mci_cmd *cmd, + unsigned int cmdat) +{ + unsigned flags = 0; + unsigned cmdval = 0; + + if (host->cmd != NULL) + dev_err(host->hw_dev, "error!\n"); + + if ((atmel_mci_readl(host, AT91_MCI_SR) & AT91_MCI_CMDRDY) == 0) { + dev_err(host->hw_dev, "mci not ready!\n"); + return -EBUSY; + } + + host->cmd = cmd; + cmdval = AT91_MCI_CMDNB & cmd->cmdidx; + + switch (cmd->resp_type) { + case MMC_RSP_R1: /* short CRC, OPCODE */ + case MMC_RSP_R1b:/* short CRC, OPCODE, BUSY */ + flags |= AT91_MCI_RSPTYP_48; + break; + case MMC_RSP_R2: /* long 136 bit + CRC */ + flags |= AT91_MCI_RSPTYP_136; + break; + case MMC_RSP_R3: /* short */ + flags |= AT91_MCI_RSPTYP_48; + break; + case MMC_RSP_NONE: + flags |= AT91_MCI_RSPTYP_NONE; + break; + default: + dev_err(host->hw_dev, "unhandled response type 0x%x\n", + cmd->resp_type); + return -EINVAL; + } + cmdval |= AT91_MCI_RSPTYP & flags; + cmdval |= cmdat & ~(AT91_MCI_CMDNB | AT91_MCI_RSPTYP); + + atmel_mci_writel(host, AT91_MCI_ARGR, cmd->cmdarg); + atmel_mci_writel(host, AT91_MCI_CMDR, cmdval); + + return 0; +} + +/** init the host interface */ +static int mci_reset(struct mci_host *mci, struct device_d *mci_dev) +{ + int ret; + struct atmel_mci_host *host = to_mci_host(mci); + struct atmel_mci_platform_data *pd = host->hw_dev->platform_data; + + ret = gpio_get_value(pd->detect_pin); + dev_dbg(host->hw_dev, "card %sdetected\n", ret != 0 ? "not " : ""); + + if (pd->detect_pin && ret == 1) + return -ENODEV; + + clk_enable(host->clk); + atmel_mci_reset(host); + + return 0; +} + +/** change host interface settings */ +static void mci_set_ios(struct mci_host *mci, struct device_d *mci_dev, + unsigned bus_width, unsigned clock) +{ + struct atmel_mci_host *host = to_mci_host(mci); + + dev_dbg(host->hw_dev, "atmel_mci_set_ios: bus_width=%d clk=%d\n", + bus_width, clock); + + switch (bus_width) { + case 4: + atmel_mci_writel(host, AT91_MCI_SDCR, AT91_MCI_SDCBUS_4BIT); + break; + case 8: + atmel_mci_writel(host, AT91_MCI_SDCR, AT91_MCI_SDCBUS_8BIT); + break; + default: + atmel_mci_writel(host, AT91_MCI_SDCR, AT91_MCI_SDCBUS_1BIT); + break; + } + + if (clock) { + atmel_set_clk_rate(host, clock); + atmel_mci_writel(host, AT91_MCI_CR, AT91_MCI_MCIEN + ); + } else { + atmel_mci_writel(host, AT91_MCI_CR, AT91_MCI_MCIDIS); + } + + return; +} + +/** handle a command */ +static int mci_request(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data) +{ + struct atmel_mci_host *host = to_mci_host(mci); + u32 stat, cmdat = 0; + int ret; + + if (cmd->resp_type != MMC_RSP_NONE) + cmdat |= AT91_MCI_MAXLAT; + + if (data) { + atmel_setup_data(host, data); + + cmdat |= AT91_MCI_TRCMD_START | AT91_MCI_TRTYP_MULTIPLE; + + if (data->flags & MMC_DATA_READ) + cmdat |= AT91_MCI_TRDIR_RX; + } + + ret = atmel_start_cmd(host, cmd, cmdat); + if (ret) { + atmel_finish_request(host); + return ret; + } + + stat = atmel_poll_status(host, AT91_MCI_CMDRDY); + return atmel_cmd_done(host, stat); +} + +#ifdef CONFIG_MCI_INFO +static void mci_info(struct device_d *mci_dev) +{ + struct atmel_mci_host *host = mci_dev->priv; + struct atmel_mci_platform_data *pd = host->hw_dev->platform_data; + + printf(" Bus data width: %d bit\n", host->mci.bus_width); + + printf(" Bus frequency: %u Hz\n", host->mci.clock); + printf(" Frequency limits: "); + if (host->mci.f_min == 0) + printf("no lower limit "); + else + printf("%u Hz lower limit ", host->mci.f_min); + if (host->mci.f_max == 0) + printf("- no upper limit"); + else + printf("- %u Hz upper limit", host->mci.f_max); + + printf("\n Card detection support: %s\n", + pd->detect_pin != 0 ? "yes" : "no"); + +} +#endif /* CONFIG_MCI_INFO */ + +static int mci_probe(struct device_d *hw_dev) +{ + unsigned long clk_rate; + struct atmel_mci_host *host; + struct atmel_mci_platform_data *pd = hw_dev->platform_data; + + if (!pd) { + dev_err(hw_dev, "missing platform data\n"); + return -EINVAL; + } + + host = xzalloc(sizeof(*host)); + host->mci.send_cmd = mci_request; + host->mci.set_ios = mci_set_ios; + host->mci.init = mci_reset; + + host->mci.host_caps = pd->host_caps; + if (pd->bus_width >= 4) + host->mci.host_caps |= MMC_MODE_4BIT; + if (pd->bus_width == 8) + host->mci.host_caps |= MMC_MODE_8BIT; + + host->base = (void __iomem *)hw_dev->map_base; + host->hw_dev = hw_dev; + hw_dev->priv = host; + host->clk = clk_get(hw_dev, "mci_clk"); + if (host->clk == NULL) { + dev_err(hw_dev, "no mci_clk\n"); + return -EINVAL; + } + + clk_rate = clk_get_rate(host->clk); + + host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + + host->mci.f_min = clk_rate >> 9; + host->mci.f_max = clk_rate >> 1; + + mci_register(&host->mci); + + return 0; +} + +static struct driver_d atmel_mci_driver = { + .name = "atmel_mci", + .probe = mci_probe, +#ifdef CONFIG_MCI_INFO + .info = mci_info, +#endif +}; + +static int atmel_mci_init_driver(void) +{ + register_driver(&atmel_mci_driver); + return 0; +} + +device_initcall(atmel_mci_init_driver); diff --git a/drivers/net/macb.c b/drivers/net/macb.c index df3b6af..bc6618b 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -431,7 +431,7 @@ edev->send = macb_send; edev->recv = macb_recv; edev->halt = macb_halt; - edev->get_ethaddr = macb_get_ethaddr; + edev->get_ethaddr = pdata->get_ethaddr ? pdata->get_ethaddr : macb_get_ethaddr; edev->set_ethaddr = macb_set_ethaddr; macb->miidev.read = macb_phy_read; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 9e05f4b..b8ae475 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -48,6 +48,11 @@ default y bool "Altera serial driver" +config DRIVER_SERIAL_ALTERA_JTAG + depends on NIOS2 + default n + bool "Altera JTAG serial driver" + config DRIVER_SERIAL_NS16550 default n bool "NS16550 serial driver" diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index df9e705..8b56d15 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_DRIVER_SERIAL_PL010) += serial_pl010.o obj-$(CONFIG_DRIVER_SERIAL_S3C24X0) += serial_s3c24x0.o obj-$(CONFIG_DRIVER_SERIAL_ALTERA) += serial_altera.o +obj-$(CONFIG_DRIVER_SERIAL_ALTERA_JTAG) += serial_altera_jtag.o diff --git a/drivers/serial/serial_altera_jtag.c b/drivers/serial/serial_altera_jtag.c new file mode 100644 index 0000000..322f9e9 --- /dev/null +++ b/drivers/serial/serial_altera_jtag.c @@ -0,0 +1,101 @@ +/* + * (C) Copyright 2004, Psyent Corporation + * Scott McNutt + * + * (C) Copyright 2011 - Franck JULLIEN + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +static int altera_serial_jtag_setbaudrate(struct console_device *cdev, int baudrate) +{ + return 0; +} + +static void altera_serial_jtag_putc(struct console_device *cdev, char c) +{ + struct nios_jtag *jtag = (struct nios_jtag *)cdev->dev->map_base; + uint32_t st; + + while (1) { + st = readl(&jtag->control); + if (NIOS_JTAG_WSPACE(st)) + break; + } + + writel(c, &jtag->data); +} + +static int altera_serial_jtag_tstc(struct console_device *cdev) +{ + struct nios_jtag *jtag = (struct nios_jtag *)cdev->dev->map_base; + + return readl(&jtag->control) & NIOS_JTAG_RRDY; +} + +static int altera_serial_jtag_getc(struct console_device *cdev) +{ + struct nios_jtag *jtag = (struct nios_jtag *)cdev->dev->map_base; + uint32_t val; + + while (1) { + val = readl(&jtag->data); + if (val & NIOS_JTAG_RVALID) + break; + } + + return val & 0xFF; +} + +static int altera_serial_jtag_probe(struct device_d *dev) { + + struct console_device *cdev; + + cdev = malloc(sizeof(struct console_device)); + dev->type_data = cdev; + cdev->dev = dev; + cdev->f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR; + cdev->tstc = altera_serial_jtag_tstc; + cdev->putc = altera_serial_jtag_putc; + cdev->getc = altera_serial_jtag_getc; + cdev->setbrg = altera_serial_jtag_setbaudrate; + + console_register(cdev); + + return 0; +} + +static struct driver_d altera_serial_jtag_driver = { + .name = "altera_serial_jtag", + .probe = altera_serial_jtag_probe, +}; + +static int altera_serial_jtag_init(void) +{ + return register_driver(&altera_serial_jtag_driver); +} + +console_initcall(altera_serial_jtag_init); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 4560259..6456897 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -75,6 +75,7 @@ proxy->chip_select = chip->chip_select; proxy->max_speed_hz = chip->max_speed_hz; proxy->mode = chip->mode; + proxy->dev.platform_data = chip->platform_data; strcpy(proxy->dev.name, chip->name); proxy->dev.type_data = proxy; status = register_device(&proxy->dev); @@ -194,3 +195,44 @@ return spi->master->transfer(spi, message); } +/** + * spi_write_then_read - SPI synchronous write followed by read + * @spi: device with which data will be exchanged + * @txbuf: data to be written + * @n_tx: size of txbuf, in bytes + * @rxbuf: buffer into which data will be read + * @n_rx: size of rxbuf, in bytes + * Context: can sleep + * + * This performs a half duplex MicroWire style transaction with the + * device, sending txbuf and then reading rxbuf. The return value + * is zero for success, else a negative errno status code. + * This call may only be used from a context that may sleep. + */ +int spi_write_then_read(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx) +{ + int status; + struct spi_message message; + struct spi_transfer x[2]; + + spi_message_init(&message); + memset(x, 0, sizeof x); + if (n_tx) { + x[0].len = n_tx; + spi_message_add_tail(&x[0], &message); + } + if (n_rx) { + x[1].len = n_rx; + spi_message_add_tail(&x[1], &message); + } + + x[0].tx_buf = txbuf; + x[1].rx_buf = rxbuf; + + /* do the i/o */ + status = spi_sync(spi, &message); + return status; +} +EXPORT_SYMBOL(spi_write_then_read); diff --git a/include/clock.h b/include/clock.h index b9acdb9..af5b939 100644 --- a/include/clock.h +++ b/include/clock.h @@ -25,6 +25,8 @@ uint64_t get_time_ns(void); +void clocks_calc_mult_shift(uint32_t *mult, uint32_t *shift, uint32_t from, uint32_t to, uint32_t maxsec); + uint32_t clocksource_hz2mult(uint32_t hz, uint32_t shift_constant); int is_timeout(uint64_t start_ns, uint64_t time_offset_ns); diff --git a/include/linux/stat.h b/include/linux/stat.h index 10103d4..bc7dce4 100644 --- a/include/linux/stat.h +++ b/include/linux/stat.h @@ -42,31 +42,6 @@ #define S_IWOTH 00002 /* read permission for other */ #define S_IXOTH 00001 /* execute/search permission for other */ -#ifdef __PPC__ - -struct stat { - dev_t st_dev; /* file system id */ - ino_t st_ino; /* file id */ - mode_t st_mode; /* ownership/protection */ - nlink_t st_nlink; /* number of links */ - uid_t st_uid; /* user id */ - gid_t st_gid; /* group id */ - dev_t st_rdev; - off_t st_size; /* file size in # of bytes */ - unsigned long st_blksize; /* block size */ - unsigned long st_blocks; /* file size in # of blocks */ - unsigned long st_atime; /* time file was last accessed */ - unsigned long __unused1; - unsigned long st_mtime; /* time file was last modified */ - unsigned long __unused2; - unsigned long st_ctime; /* time file status was last changed */ - unsigned long __unused3; - unsigned long __unused4; - unsigned long __unused5; -}; - -#else - struct stat { unsigned short st_dev; unsigned short __pad1; @@ -90,39 +65,6 @@ unsigned long __unused5; }; -#endif /* __ARM__ */ - -#if defined (__MIPS__) - -struct stat { - dev_t st_dev; - long st_pad1[3]; - ino_t st_ino; - mode_t st_mode; - nlink_t st_nlink; - uid_t st_uid; - gid_t st_gid; - dev_t st_rdev; - long st_pad2[2]; - off_t st_size; - long st_pad3; - /* - * Actually this should be timestruc_t st_atime, st_mtime and st_ctime - * but we don't have it under Linux. - */ - time_t st_atime; - long reserved0; - time_t st_mtime; - long reserved1; - time_t st_ctime; - long reserved2; - long st_blksize; - long st_blocks; - long st_pad4[14]; -}; - -#endif /* __MIPS__ */ - #ifdef __cplusplus } #endif diff --git a/include/spi/eeprom.h b/include/spi/eeprom.h new file mode 100644 index 0000000..15495e5 --- /dev/null +++ b/include/spi/eeprom.h @@ -0,0 +1,22 @@ +#ifndef _SPI_EEPROM_H +#define _SPI_EEPROM_H + +/* + * Put one of these structures in platform_data for SPI EEPROMS handled + * by the "at25" driver. On SPI, most EEPROMS understand the same core + * command set. If you need to support EEPROMs that don't yet fit, add + * flags to support those protocol options. These values all come from + * the chip datasheets. + */ +struct spi_eeprom { + char name[10]; + u16 page_size; /* for writes */ + u16 flags; +#define EE_ADDR1 0x0001 /* 8 bit addrs */ +#define EE_ADDR2 0x0002 /* 16 bit addrs */ +#define EE_ADDR3 0x0004 /* 24 bit addrs */ +#define EE_READONLY 0x0008 /* disallow writes */ + u32 size; +}; + +#endif /* _SPI_EEPROM_H */ diff --git a/include/spi/spi.h b/include/spi/spi.h index 8dce8db..ac2013a 100644 --- a/include/spi/spi.h +++ b/include/spi/spi.h @@ -14,8 +14,8 @@ /* mode becomes spi_device.mode, and is essential for chips * where the default of SPI_CS_HIGH = 0 is wrong. */ - u8 mode; - + u8 mode; + void *platform_data; }; /** @@ -349,6 +349,80 @@ } #endif +/** + * spi_write - SPI synchronous write + * @spi: device to which data will be written + * @buf: data buffer + * @len: data buffer size + * Context: can sleep + * + * This writes the buffer and returns zero or a negative error code. + * Callable only from contexts that can sleep. + */ +static inline int +spi_write(struct spi_device *spi, const void *buf, size_t len) +{ + struct spi_transfer t = { + .tx_buf = buf, + .len = len, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + return spi_sync(spi, &m); +} + +/** + * spi_read - SPI synchronous read + * @spi: device from which data will be read + * @buf: data buffer + * @len: data buffer size + * Context: can sleep + * + * This reads the buffer and returns zero or a negative error code. + * Callable only from contexts that can sleep. + */ +static inline int +spi_read(struct spi_device *spi, void *buf, size_t len) +{ + struct spi_transfer t = { + .rx_buf = buf, + .len = len, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + return spi_sync(spi, &m); +} + +/* this copies txbuf and rxbuf data; for small transfers only! */ +extern int spi_write_then_read(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx); + +/** + * spi_w8r8 - SPI synchronous 8 bit write followed by 8 bit read + * @spi: device with which data will be exchanged + * @cmd: command to be written before data is read back + * Context: can sleep + * + * This returns the (unsigned) eight bit number returned by the + * device, or else a negative error code. Callable only from + * contexts that can sleep. + */ +static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd) +{ + ssize_t status; + u8 result; + + status = spi_write_then_read(spi, &cmd, 1, &result, 1); + + /* return negative errno or unsigned value */ + return (status < 0) ? status : result; +} + #endif /* DOXYGEN_SHOULD_SKIP_THIS */ #endif /* __INCLUDE_SPI_H */ diff --git a/lib/readline.c b/lib/readline.c index a3e1de9..5717a17 100644 --- a/lib/readline.c +++ b/lib/readline.c @@ -176,7 +176,6 @@ { unsigned long num = 0; unsigned long eol_num = 0; - unsigned long rlen; unsigned wlen; int ichar; int insert = 1; @@ -191,7 +190,6 @@ puts (prompt); while (1) { - rlen = 1; ichar = read_key(); if ((ichar == '\n') || (ichar == '\r')) {