diff --git a/arch/arm/mach-ns9xxx/cc9p9215_devices.c b/arch/arm/mach-ns9xxx/cc9p9215_devices.c index a7939d7cb5764d459948293d430cc2693bb01f35..eff277fdc6c8d862027abdb19902fe9ebd5b9aeb 100644 --- a/arch/arm/mach-ns9xxx/cc9p9215_devices.c +++ b/arch/arm/mach-ns9xxx/cc9p9215_devices.c @@ -122,19 +122,21 @@ void __init ns9xxx_add_device_cc9p9215_flash(void) {} #endif #if defined(CONFIG_SPI_NS921X) || defined(CONFIG_SPI_NS921X_MODULE) -void __init ns9xxx_add_device_cc9p9215_spi(struct ns921x_data *pdata) -{ - int gpio[] = {3, 5, 7, 0}; - - if (pdata) - ns9xxx_add_device_ns921x_spi(gpio, 4, - ARRAY_SIZE(gpio) - 1, pdata); - else - ns9xxx_add_device_ns921x_spi(gpio, 4, - ARRAY_SIZE(gpio), pdata); +/* SPI ports and their related GPIOs */ +static struct spi_ns9xxx_data ns9xxx_device_cc9p9215_spi_data = { + .gpios = {7, 3, 5, 0}, + .gpio_funcs = { NS921X_GPIO_FUNC_4, + NS921X_GPIO_FUNC_4, + NS921X_GPIO_FUNC_4, + NS921X_GPIO_FUNC_4 }, + .nr_gpios = 4, +}; + +void __init ns9xxx_add_device_cc9p9215_spi(void) { + ns9xxx_add_device_ns921x_spi(&ns9xxx_device_cc9p9215_spi_data); } #else -void __init ns9xxx_add_device_cc9p9215_spi(struct ns921x_data *pdata) {} +void __init ns9xxx_add_device_cc9p9215_spi(void) {} #endif diff --git a/arch/arm/mach-ns9xxx/cc9p9215_devices.h b/arch/arm/mach-ns9xxx/cc9p9215_devices.h index 1fd0a273e0b2cba9ead3c591e105219a03818f35..0581a36b3cc159933d7d5ae7640f9aace55e8c77 100644 --- a/arch/arm/mach-ns9xxx/cc9p9215_devices.h +++ b/arch/arm/mach-ns9xxx/cc9p9215_devices.h @@ -16,7 +16,7 @@ void __init ns9xxx_add_device_cc9p9215_uartb(int gpio_start); void __init ns9xxx_add_device_cc9p9215_uartc(int gpio_start); void __init ns9xxx_add_device_cc9p9215_uartd(int gpio_start); void __init ns9xxx_add_device_cc9p9215_flash(void); -void __init ns9xxx_add_device_cc9p9215_spi(struct ns921x_data *pdata); +void __init ns9xxx_add_device_cc9p9215_spi(void); #define ns9xxx_add_device_cc9p9215_uarta_rxtx() \ ns9xxx_add_device_cc9p9215_uarta(2) diff --git a/arch/arm/mach-ns9xxx/cc9p9360_devices.c b/arch/arm/mach-ns9xxx/cc9p9360_devices.c index fec988537a9aa2105193c95d1874ffde12a56edd..bc1dc5e99152d1d3dbfc035fd6ff0a8f25ac2966 100644 --- a/arch/arm/mach-ns9xxx/cc9p9360_devices.c +++ b/arch/arm/mach-ns9xxx/cc9p9360_devices.c @@ -16,6 +16,7 @@ #include "ns9360_devices.h" #include "cc9p9360_devices.h" + #if defined(CONFIG_MTD_NAND_CCX9X) || defined(CONFIG_MTD_NAND_CCX9X_MODULE) static struct ccx9x_nand_info ns9xxx_device_cc9p9360_nand_data = { .addr_offset = 0x2000, @@ -162,28 +163,61 @@ void __init ns9xxx_add_device_cc9p9360_fb(int power) {} #endif #if defined(CONFIG_SPI_NS9360) || defined(CONFIG_SPI_NS9360_MODULE) +/* SPI ports and their related GPIOs */ +static struct spi_ns9xxx_data ns9xxx_device_cc9p9360_spi_porta_data = { + .gpios = {8, 9, 14, 15}, + .gpio_funcs = { NS9360_GPIO_FUNC_0, + NS9360_GPIO_FUNC_0, + NS9360_GPIO_FUNC_0, + NS9360_GPIO_FUNC_0 }, + .nr_gpios = 4, +}; + +static struct spi_ns9xxx_data ns9xxx_device_cc9p9360_spi_portb_data = { + .gpios = {0, 1, 6, 7}, + .gpio_funcs = { NS9360_GPIO_FUNC_0, + NS9360_GPIO_FUNC_0, + NS9360_GPIO_FUNC_0, + NS9360_GPIO_FUNC_0 }, + .nr_gpios = 4, +}; + +static struct spi_ns9xxx_data ns9xxx_device_cc9p9360_spi_portc_data = { + .gpios = {40, 41, 22, 23}, + .gpio_funcs = { NS9360_GPIO_FUNC_0, + NS9360_GPIO_FUNC_0, + NS9360_GPIO_FUNC_0, + NS9360_GPIO_FUNC_0 }, + .nr_gpios = 4, +}; + +static struct spi_ns9xxx_data ns9xxx_device_cc9p9360_spi_portd_data = { + .gpios = {44, 45, 26, 27}, + .gpio_funcs = { NS9360_GPIO_FUNC_0, + NS9360_GPIO_FUNC_0, + NS9360_GPIO_FUNC_0, + NS9360_GPIO_FUNC_0 }, + .nr_gpios = 4, +}; + void __init ns9xxx_add_device_cc9p9360_spi_porta(void) { - int gpio[] = {8, 9, 14, 15}; - ns9xxx_add_device_ns9360_spi_porta(gpio, 0); + ns9xxx_add_device_ns9360_spi_porta( &ns9xxx_device_cc9p9360_spi_porta_data ); } void __init ns9xxx_add_device_cc9p9360_spi_portb(void) { - int gpio[] = {0, 1, 6, 7}; - ns9xxx_add_device_ns9360_spi_portb(gpio, 0); + ns9xxx_add_device_ns9360_spi_portb( &ns9xxx_device_cc9p9360_spi_portb_data ); } void __init ns9xxx_add_device_cc9p9360_spi_portc(void) { - int gpio[] = {40, 41, 22, 23}; - ns9xxx_add_device_ns9360_spi_portc(gpio, 0); + ns9xxx_add_device_ns9360_spi_portc( &ns9xxx_device_cc9p9360_spi_portc_data ); } void __init ns9xxx_add_device_cc9p9360_spi_portd(void) { - int gpio[] = {44, 45, 26, 27}; - ns9xxx_add_device_ns9360_spi_portd(gpio, 0); + ns9xxx_add_device_ns9360_spi_portd( &ns9xxx_device_cc9p9360_spi_portd_data ); } #else diff --git a/arch/arm/mach-ns9xxx/ccx9c_devices.c b/arch/arm/mach-ns9xxx/ccx9c_devices.c index 30aca7a72551395a51273bf57ec2496c3157e80b..24f02aa144adb7eab7dd27739d45164e0577de2e 100644 --- a/arch/arm/mach-ns9xxx/ccx9c_devices.c +++ b/arch/arm/mach-ns9xxx/ccx9c_devices.c @@ -18,6 +18,29 @@ #include "ns9360_devices.h" #include "ccx9c_devices.h" +#if defined(CONFIG_SPI_NS9360) || defined(CONFIG_SPI_NS9360_MODULE) +/* SPI ports and their related GPIOs */ +static struct spi_ns9xxx_data ns9xxx_device_ccx9c_spi_porta_data = { + .gpios = {8, 9, 14, 15}, + .nr_gpios = 4, +}; + +static struct spi_ns9xxx_data ns9xxx_device_ccx9c_spi_portb_data = { + .gpios = {0, 1, 6, 7}, + .nr_gpios = 4, +}; + +static struct spi_ns9xxx_data ns9xxx_device_ccx9c_spi_portc_data = { + .gpios = {40, 41, 22, 23}, + .nr_gpios = 4, +}; + +static struct spi_ns9xxx_data ns9xxx_device_ccx9c_spi_portd_data = { + .gpios = {44, 45, 26, 27}, + .nr_gpios = 4, +}; +#endif + #if defined(CONFIG_MTD_NAND_CCX9X) || defined(CONFIG_MTD_NAND_CCX9X_MODULE) static struct ccx9x_nand_info ns9xxx_device_ccx9c_nand_data = { .addr_offset = 0x8000, @@ -165,26 +188,22 @@ void __init ns9xxx_add_device_ccx9c_fb(int power) {} #if defined(CONFIG_SPI_NS9360) || defined(CONFIG_SPI_NS9360_MODULE) void __init ns9xxx_add_device_ccx9c_spi_porta(void) { - int gpio[] = {8, 9, 14, 15}; - ns9xxx_add_device_ns9360_spi_porta(gpio, 0); + ns9xxx_add_device_ns9360_spi_porta( &ns9xxx_device_ccx9c_spi_porta_data ); } void __init ns9xxx_add_device_ccx9c_spi_portb(void) { - int gpio[] = {0, 1, 6, 7}; - ns9xxx_add_device_ns9360_spi_portb(gpio, 0); + ns9xxx_add_device_ns9360_spi_portb( &ns9xxx_device_ccx9c_spi_portb_data ); } void __init ns9xxx_add_device_ccx9c_spi_portc(void) { - int gpio[] = {40, 41, 22, 23}; - ns9xxx_add_device_ns9360_spi_portc(gpio, 0); + ns9xxx_add_device_ns9360_spi_portc( &ns9xxx_device_ccx9c_spi_portc_data ); } void __init ns9xxx_add_device_ccx9c_spi_portd(void) { - int gpio[] = {44, 45, 26, 27}; - ns9xxx_add_device_ns9360_spi_portd(gpio, 0); + ns9xxx_add_device_ns9360_spi_portd( &ns9xxx_device_ccx9c_spi_portd_data ); } #else diff --git a/arch/arm/mach-ns9xxx/cme9210_devices.c b/arch/arm/mach-ns9xxx/cme9210_devices.c index 101f94e1d03d93bea0159413402cd42e2564ec95..b7ea433b47ef7eec2b6ca47cafdcd76d62892a3d 100644 --- a/arch/arm/mach-ns9xxx/cme9210_devices.c +++ b/arch/arm/mach-ns9xxx/cme9210_devices.c @@ -1,4 +1,4 @@ -/* + /* * arch/arm/mach-ns9xxx/cme9210_devices.c * * Copyright (C) 2008 by Digi International Inc. @@ -51,19 +51,22 @@ void __init ns9xxx_add_device_cme9210_flash(void) {} #endif #if defined(CONFIG_SPI_NS921X) || defined(CONFIG_SPI_NS921X_MODULE) -void __init ns9xxx_add_device_cme9210_spi(struct ns921x_data *pdata) -{ - int gpio[] = {3, 5, 7, 0}; +/* SPI ports and their related GPIOs */ +static struct spi_ns9xxx_data ns9xxx_device_cme9210_spi_data = { + .gpios = {7, 3, 5, 2}, + .gpio_funcs = { NS921X_GPIO_FUNC_4, + NS921X_GPIO_FUNC_4, + NS921X_GPIO_FUNC_4, + NS921X_GPIO_FUNC_GPIO }, + .nr_gpios = 4, +}; - if (pdata) - ns9xxx_add_device_ns921x_spi(gpio, 4, - ARRAY_SIZE(gpio) - 1, pdata); - else - ns9xxx_add_device_ns921x_spi(gpio, 4, - ARRAY_SIZE(gpio), pdata); +void __init ns9xxx_add_device_cme9210_spi(void) +{ + ns9xxx_add_device_ns921x_spi(&ns9xxx_device_cme9210_spi_data); } #else -void __init ns9xxx_add_device_cme9210_spi(struct ns921x_data *pdata) {} +void __init ns9xxx_add_device_cme9210_spi(void) {} #endif diff --git a/arch/arm/mach-ns9xxx/cme9210_devices.h b/arch/arm/mach-ns9xxx/cme9210_devices.h index 545f6d4bbc904eb75ea5062e9f69f1deb09225d6..5b463c3200c76aeb92f900b5edab917a73bca50e 100644 --- a/arch/arm/mach-ns9xxx/cme9210_devices.h +++ b/arch/arm/mach-ns9xxx/cme9210_devices.h @@ -12,7 +12,7 @@ void __init ns9xxx_add_device_cme9210_eth(void); void __init ns9xxx_add_device_cme9210_uarta(int gpio_nr); void __init ns9xxx_add_device_cme9210_flash(void); -void __init ns9xxx_add_device_cme9210_spi(struct ns921x_data *pdata); +void __init ns9xxx_add_device_cme9210_spi(void); #define ns9xxx_add_device_cme9210_uarta_rxtx() \ ns9xxx_add_device_cme9210_uarta(2) diff --git a/arch/arm/mach-ns9xxx/mach-cc9p9215js.c b/arch/arm/mach-ns9xxx/mach-cc9p9215js.c index e334eef91205ea226c2d5e1ddb2f4911a2308a26..48dfa118c8841eaffa95ac08fc4a1ca44bf56de6 100644 --- a/arch/arm/mach-ns9xxx/mach-cc9p9215js.c +++ b/arch/arm/mach-ns9xxx/mach-cc9p9215js.c @@ -90,7 +90,7 @@ static void __init mach_cc9p9215js_init_machine(void) /* SPI */ #ifdef CONFIG_CC9P9215JS_SPI - ns9xxx_add_device_cc9p9215_spi(NULL); + ns9xxx_add_device_cc9p9215_spi(); #endif /* SPI devices */ diff --git a/arch/arm/mach-ns9xxx/mach-cme9210js.c b/arch/arm/mach-ns9xxx/mach-cme9210js.c index 18b74bf9018332b73a5b09592de646171fd81356..84b591d50e3c43c7c158747abd4309baeae934a1 100644 --- a/arch/arm/mach-ns9xxx/mach-cme9210js.c +++ b/arch/arm/mach-ns9xxx/mach-cme9210js.c @@ -9,8 +9,12 @@ * the Free Software Foundation. */ +#include +#include #include +#include #include +#include #include #include @@ -20,18 +24,48 @@ #include "ns921x_devices.h" #include "cme9210_devices.h" +#if defined(CONFIG_MTD_M25P80) || defined(CONFIG_MTD_M25P80_MODULE) +static struct mtd_partition spi_flash_partitions[] = { + { + .name = "part1", + .offset = 0, + .size = 0x100000, + }, { + .name = "part2", + .offset = 0x100000, + .size = 0xF00000, + }, +}; +static struct flash_platform_data spi_flash_data = { + .name = "m25p80", + .parts = spi_flash_partitions, + .nr_parts = ARRAY_SIZE(spi_flash_partitions), + .type = "m25p128", +}; +#endif + /* SPI devices */ static struct spi_board_info spi_devices[] __initdata = { -#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) +#if defined(CONFIG_MTD_M25P80) || defined(CONFIG_MTD_M25P80_MODULE) { - .modalias = "spidev", - .max_speed_hz = 33314133, - .bus_num = 1, - .chip_select = 0, + .modalias = "m25p80", + .max_speed_hz = 10000000, + .bus_num = 1, + .chip_select = 0, + .platform_data = &spi_flash_data, }, #endif }; + +/* W1 master */ +#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) +static struct w1_gpio_platform_data ns9210js_w1_gpio_pdata = { + .pin = 6, /* pin 10 / ext. GPIO5 / GPIO6 */ + .is_open_drain = 0, +}; +#endif + static void __init mach_cme9210js_init_machine(void) { /* register several system clocks */ @@ -57,12 +91,17 @@ static void __init mach_cme9210js_init_machine(void) /* SPI */ #ifdef CONFIG_CME9210JS_SPI - ns9xxx_add_device_cme9210_spi(NULL); + ns9xxx_add_device_cme9210_spi(); #endif /* SPI devices */ spi_register_board_info(spi_devices, ARRAY_SIZE(spi_devices)); + /* w1 device */ +#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) + ns9xxx_add_device_ns921x_w1(&ns9210js_w1_gpio_pdata); +#endif + /* Init the FIM devices */ ns9xxx_add_device_ns921x_fims(); } diff --git a/arch/arm/mach-ns9xxx/ns921x_devices.c b/arch/arm/mach-ns9xxx/ns921x_devices.c index f45ecebe4dfe6a68fb9efe30b66457c6542a7e78..f5b51e14de3c15869935e62e2e84e2f9c95b2f95 100644 --- a/arch/arm/mach-ns9xxx/ns921x_devices.c +++ b/arch/arm/mach-ns9xxx/ns921x_devices.c @@ -15,15 +15,33 @@ #include #include #include +#include #include #include #include +#include #include "clock.h" #include "ns921x_devices.h" #include "processor-ns921x.h" +#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) +static struct platform_device ns9210js_w1_device = { + .name = "w1-gpio", + .id = -1, +}; + +void __init ns9xxx_add_device_ns921x_w1(struct w1_gpio_platform_data *w1_data) +{ + ns9210js_w1_device.dev.platform_data = w1_data; + platform_device_register(&ns9210js_w1_device); +} +#else +void __init ns9xxx_add_device_ns921x_w1( + struct w1_gpio_platform_data *w1_data) {}; +#endif + /* Watchdog timer */ #if defined(CONFIG_NS9XXX_WATCHDOG) || defined(CONFIG_NS9XXX_WATCHDOG_MODULE) static struct clk wdt_clk = { @@ -434,21 +452,26 @@ void __init ns9xxx_add_device_ns921x_flash( struct physmap_flash_data *flash_data) {} #endif +/* SPI port */ #if defined(CONFIG_SPI_NS921X) || defined(CONFIG_SPI_NS921X_MODULE) -int __init spi_register_gpios(int gpio[], int func, int gpio_count) +int __init spi_register_gpios(struct spi_ns9xxx_data *data) { int i; - for (i = 0; i < gpio_count; i++) { - if (gpio_request(gpio[i], "spi_ns921x")) + for (i = 0; i < data->nr_gpios; i++) { + if (gpio_request(data->gpios[i], "spi_ns921x")) goto err; - gpio_configure_ns921x(gpio[i], 0, 0, func, 0); + gpio_configure_ns921x(data->gpios[i], + 0, + 0, + data->gpio_funcs[i], + 0 ); } return 0; err: for (; i >= 0; i--) - gpio_free(gpio[i]); + gpio_free(data->gpios[i]); return -EBUSY; } @@ -474,17 +497,22 @@ static struct resource spi_resources[] = { }, }; +static u64 spi_dmamask = DMA_BIT_MASK(32); + static struct platform_device ns9xxx_device_ns921x_spi = { .name = "spi_ns921x", .id = 1, .resource = spi_resources, .num_resources = ARRAY_SIZE(spi_resources), + .dev = { + .dma_mask = &spi_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, }; -void __init ns9xxx_add_device_ns921x_spi(int gpio[], int func, - int gpio_count, struct ns921x_data *pdata) +void __init ns9xxx_add_device_ns921x_spi(struct spi_ns9xxx_data *data) { - if (spi_register_gpios(gpio, func, gpio_count)) + if (spi_register_gpios(data)) return; spi_clk.clk.parent = clk_get(NULL, "systemclock"); @@ -494,12 +522,11 @@ void __init ns9xxx_add_device_ns921x_spi(int gpio[], int func, if (clk_register(&spi_clk.clk)) return; - ns9xxx_device_ns921x_spi.dev.platform_data = pdata; + ns9xxx_device_ns921x_spi.dev.platform_data = data; platform_device_register(&ns9xxx_device_ns921x_spi); } #else -void __init ns9xxx_add_device_ns921x_spi(int gpio[], int func, - int gpio_count, struct ns921x_data *pdata) {} +void __init ns9xxx_add_device_ns921x_spi(struct spi_ns9xxx_data *data) {} #endif #if defined(CONFIG_PM) @@ -552,7 +579,7 @@ int __init ns921x_extgpio_pm_wakeup_init(unsigned int gpio) goto err_enable_wake; } return 0; - + err_enable_wake: free_irq(map->irq, NULL); err_irq_request: @@ -602,7 +629,7 @@ void __init ns9xxx_add_device_ns921x_fim_serial(void) #else fims = CONFIG_FIM_SERIAL_NUMBER; #endif - + if (fims == 0) platform_device_register(&ns921x_fim_serial0); else if (fims == 1) diff --git a/arch/arm/mach-ns9xxx/ns921x_devices.h b/arch/arm/mach-ns9xxx/ns921x_devices.h index 2e5c26399d9aeb9e6248476707454893ee5f2867..4c8fe7705779f2d09bdafe6b5bf0492884abcc98 100644 --- a/arch/arm/mach-ns9xxx/ns921x_devices.h +++ b/arch/arm/mach-ns9xxx/ns921x_devices.h @@ -13,7 +13,8 @@ #include #include #include -#include +#include +#include #include "clock.h" @@ -31,7 +32,7 @@ void __init ns9xxx_add_device_ns921x_uartd(int gpio_start, int gpio_nr, int func); void __init ns9xxx_add_device_ns921x_flash( struct physmap_flash_data *flash_data); -void __init ns9xxx_add_device_ns921x_spi(int gpio[], int func, - int gpio_count, struct ns921x_data *pdata); +void __init ns9xxx_add_device_ns921x_spi(struct spi_ns9xxx_data *data); int __init ns921x_extgpio_pm_wakeup_init(unsigned int gpio); void __init ns9xxx_add_device_ns921x_fims(void); +void __init ns9xxx_add_device_ns921x_w1(struct w1_gpio_platform_data *w1_data); diff --git a/arch/arm/mach-ns9xxx/ns9360_devices.c b/arch/arm/mach-ns9xxx/ns9360_devices.c index 6d8ccdcfd379cf5009cec612e0f9e8af47d1507c..31a7b0d202cbb0f0cf6b52789fc0b366705d97e7 100644 --- a/arch/arm/mach-ns9xxx/ns9360_devices.c +++ b/arch/arm/mach-ns9xxx/ns9360_devices.c @@ -19,6 +19,7 @@ #include #include +#include #include "display/fb.h" #include "clock.h" @@ -592,20 +593,23 @@ void __init ns9xxx_add_device_ns9360_fb(int gpio[], int gpio_nr, #endif #if defined(CONFIG_SPI_NS9360) || defined(CONFIG_SPI_NS9360_MODULE) -int __init spi_register_gpios(int gpio[], int func) +int __init spi_register_gpios(struct spi_ns9xxx_data *data) { int i; - for (i = 0; i < 4; i++) { - if (gpio_request(gpio[i], "spi_ns9360")) + for (i = 0; i < data->nr_gpios; i++) { + if (gpio_request(data->gpios[i], "spi_ns9360")) goto err; - gpio_configure_ns9360(gpio[i], 0, 0, func); + gpio_configure_ns9360( data->gpios[i], + 0, + 0, + data->gpio_funcs[i] ); } return 0; err: for (; i >= 0; i--) - gpio_free(gpio[i]); + gpio_free(data->gpios[i]); return -EBUSY; } @@ -752,68 +756,81 @@ static struct platform_device ns9xxx_device_ns9360_spi_portd = { }, }; -void __init ns9xxx_add_device_ns9360_spi_porta(int gpio[], int func) +void __init ns9xxx_add_device_ns9360_spi_porta(struct spi_ns9xxx_data *data) { - if (spi_register_gpios(gpio, func)) + if (spi_register_gpios(data)) return; - spi_porta_clk.parent = clk_get(NULL, "ahbclock"); + spi_porta_clk.parent = clk_get(NULL, "bbusclock"); if (IS_ERR(spi_porta_clk.parent)) return; if (clk_register(&spi_porta_clk)) return; + /* Set platform data */ + ns9xxx_device_ns9360_spi_porta.dev.platform_data = data; + platform_device_register(&ns9xxx_device_ns9360_spi_porta); } -void __init ns9xxx_add_device_ns9360_spi_portb(int gpio[], int func) +void __init ns9xxx_add_device_ns9360_spi_portb( struct spi_ns9xxx_data *data ) { - if (spi_register_gpios(gpio, func)) + if (spi_register_gpios(data)) return; - spi_portb_clk.parent = clk_get(NULL, "ahbclock"); + spi_portb_clk.parent = clk_get(NULL, "bbusclock"); if (IS_ERR(spi_portb_clk.parent)) return; if (clk_register(&spi_portb_clk)) return; + /* Set platform data */ + ns9xxx_device_ns9360_spi_portb.dev.platform_data = data; + platform_device_register(&ns9xxx_device_ns9360_spi_portb); } -void __init ns9xxx_add_device_ns9360_spi_portc(int gpio[], int func) + +void __init ns9xxx_add_device_ns9360_spi_portc(struct spi_ns9xxx_data *data) { - if (spi_register_gpios(gpio, func)) + if (spi_register_gpios(data)) return; - spi_portc_clk.parent = clk_get(NULL, "ahbclock"); + spi_portc_clk.parent = clk_get(NULL, "bbusclock"); if (IS_ERR(spi_portc_clk.parent)) return; if (clk_register(&spi_portc_clk)) return; + /* Set platform data */ + ns9xxx_device_ns9360_spi_portc.dev.platform_data = data; + platform_device_register(&ns9xxx_device_ns9360_spi_portc); } -void __init ns9xxx_add_device_ns9360_spi_portd(int gpio[], int func) +void __init ns9xxx_add_device_ns9360_spi_portd(struct spi_ns9xxx_data *data) { - if (spi_register_gpios(gpio, func)) + if (spi_register_gpios(data)) return; - spi_portd_clk.parent = clk_get(NULL, "ahbclock"); + spi_portd_clk.parent = clk_get(NULL, "bbusclock"); if (IS_ERR(spi_portd_clk.parent)) return; if (clk_register(&spi_portd_clk)) return; + /* Set platform data */ + ns9xxx_device_ns9360_spi_portd.dev.platform_data = data; + platform_device_register(&ns9xxx_device_ns9360_spi_portd); } #else -void __init ns9xxx_add_device_ns9360_spi_porta(int gpio[], int func) {} -void __init ns9xxx_add_device_ns9360_spi_portb(int gpio[], int func) {} -void __init ns9xxx_add_device_ns9360_spi_portc(int gpio[], int func) {} -void __init ns9xxx_add_device_ns9360_spi_portd(int gpio[], int func) {} +void __init ns9xxx_add_device_ns9360_spi_porta(struct spi_ns9xxx_data *data) {} +void __init ns9xxx_add_device_ns9360_spi_portb(struct spi_ns9xxx_data *data) {} +void __init ns9xxx_add_device_ns9360_spi_portc(struct spi_ns9xxx_data *data) {} +void __init ns9xxx_add_device_ns9360_spi_portd(struct spi_ns9xxx_data *data) {} #endif diff --git a/arch/arm/mach-ns9xxx/ns9360_devices.h b/arch/arm/mach-ns9xxx/ns9360_devices.h index 6e3dc80fc6fd8a2a832977d84ccdb45fa625d865..e1dbcf4b06a627c3791e46c7d8934edc0e5fc53e 100644 --- a/arch/arm/mach-ns9xxx/ns9360_devices.h +++ b/arch/arm/mach-ns9xxx/ns9360_devices.h @@ -12,6 +12,7 @@ #include #include #include +#include void __init ns9xxx_add_device_ns9360_usbh(void); void __init ns9xxx_add_device_ns9360_wdt(void); @@ -25,7 +26,7 @@ void __init ns9xxx_add_device_ns9360_nand(struct ccx9x_nand_info *); void __init ns9xxx_add_device_ns9360_i2c(struct plat_ns9xxx_i2c *); void __init ns9xxx_add_device_ns9360_fb(int gpio[], int gpio_nr, int func[], int power); -void __init ns9xxx_add_device_ns9360_spi_porta(int gpio[], int func); -void __init ns9xxx_add_device_ns9360_spi_portb(int gpio[], int func); -void __init ns9xxx_add_device_ns9360_spi_portc(int gpio[], int func); -void __init ns9xxx_add_device_ns9360_spi_portd(int gpio[], int func); +void __init ns9xxx_add_device_ns9360_spi_porta(struct spi_ns9xxx_data *data); +void __init ns9xxx_add_device_ns9360_spi_portb(struct spi_ns9xxx_data *data); +void __init ns9xxx_add_device_ns9360_spi_portc(struct spi_ns9xxx_data *data); +void __init ns9xxx_add_device_ns9360_spi_portd(struct spi_ns9xxx_data *data); diff --git a/drivers/spi/spi_ns921x.c b/drivers/spi/spi_ns921x.c index baffbfa2e0f902fd17dbba0353817de1d56affdc..ab5f6d9c29673ab264a19793d437d6119fadf0ba 100644 --- a/drivers/spi/spi_ns921x.c +++ b/drivers/spi/spi_ns921x.c @@ -1,5 +1,5 @@ /* - * linux/drivers/spi/spi_ns9215.c + * linux/drivers/spi/spi_ns921x.c * * Copyright (C) 2008 by Digi International Inc. * All rights reserved. @@ -20,13 +20,14 @@ #include #include #include -#include #include #include -#include -#include +//#include +//#include #include +#include +#include /* registers */ #define DMA_IRQSTATUS (0x0000) @@ -36,14 +37,32 @@ #define DMA_TXCTL (0x0018) #define DMA_TXBUF (0x001c) #define DMA_TXIRQCFG (0x0020) + #define SPI_CONFIG (0x1000) #define SPI_CLOCK (0x1010) #define SPI_IRQENABLE (0x1020) #define SPI_IRQSTATUS (0x1024) -/* config bit fields */ +/* DMA config bit fields */ #define DMA_ENABLECHANNEL (1 << 31) -#define DMA_IRQCFG_NRIE (1 << 22) +/* DMA interrupt/FIFO status flags */ +#define DMA_IRQSTAT_RXNCIP (1 << 31) +#define DMA_IRQSTAT_RXECIP (1 << 30) +#define DMA_IRQSTAT_RXNRIP (1 << 29) +#define DMA_IRQSTAT_RXCAIP (1 << 28) +#define DMA_IRQSTAT_RXPCIP (1 << 27) +#define DMA_IRQSTAT_TXNCIP (1 << 24) +#define DMA_IRQSTAT_TXECIP (1 << 23) +#define DMA_IRQSTAT_TXNRIP (1 << 22) +#define DMA_IRQSTAT_TXCAIP (1 << 21) +/* DMA interrupt configuration flags */ +#define DMA_IRQCFG_NCIE (1 << 24) +#define DMA_IRQCFG_ECIE (1 << 23) +#define DMA_IRQCFG_NRIE (1 << 22) +#define DMA_IRQCFG_CAIE (1 << 21) +#define DMA_IRQCFG_PCIE (1 << 20) +#define DMA_IRQCFG_BLENSTAT (0xFFFF) +/* SPI config bit fields */ #define SPI_CONFIG_MASTER (1 << 0) #define SPI_CONFIG_SLAVE (1 << 1) #define SPI_CONFIG_BITORDRMSB (1 << 2) @@ -69,9 +88,12 @@ #define SPI_CLOCK_DIVISOR_SHIFT (0) #define SPI_IDLE_LEVEL 0xff -#define MAX_RATE 33000000 +#define MAX_RATE 33330000 #define MAX_BUFFER_SIZE 65532 +#define SPI_CS_ACTIVE 1 +#define SPI_CS_INACTIVE 0 + #define DRIVER_NAME "spi_ns921x" union ns9xxx_dma_desc { @@ -92,304 +114,149 @@ union ns9xxx_dma_desc { }; struct spi_ns921x { - void __iomem *ioaddr; + void __iomem *ioaddr; /* SPI DMA base address */ struct resource *mem; struct clk *clk; - struct workqueue_struct *workqueue; +// struct workqueue_struct *workqueue; struct work_struct work; spinlock_t lock; struct list_head queue; + struct spi_transfer *current_transfer; + unsigned long remaining_bytes; + unsigned dma_xfer_len; + u32 flags; wait_queue_head_t waitq; int irq; unsigned int speed; - union ns9xxx_dma_desc *rxdesc; +/* union ns9xxx_dma_desc *rxdesc; union ns9xxx_dma_desc *txdesc; dma_addr_t rxdesc_real; dma_addr_t txdesc_real; - - struct ns921x_data *gpio; +*/ + void *buf_tx; + void *buf_rx; + dma_addr_t buf_tx_dma; + dma_addr_t buf_rx_dma; + union ns9xxx_dma_desc *desc_tx; + union ns9xxx_dma_desc *desc_rx; + dma_addr_t desc_tx_dma; + dma_addr_t desc_rx_dma; + + struct spi_ns9xxx_data *pdata; + //struct tasklet_struct xmit_tasklet; }; -static int spi_irq_pending; +#define BUFFER_SIZE PAGE_SIZE +#define INVALID_DMA_ADDRESS 0xffffffff +#define SPI_RX_IDONE 0x00000001 +#define SPI_TX_IDONE 0x00000002 -static void spi_ns921x_work_one(struct spi_ns921x *pdata, struct spi_message *m) +static void spi_ns921x_chipsel(struct spi_device *spi, int active) { - struct spi_device *spi = m->spi; - struct spi_transfer *t, *nt; - int desc_nr = 0, desc_count = 0; - char *rbuf, *tbuf; - dma_addr_t map, rmap; - int i, tlen, cs = spi->chip_select; - - iowrite32(0, pdata->ioaddr + DMA_RXCTL); - iowrite32(0, pdata->ioaddr + DMA_TXCTL); - - /* count transfers */ - list_for_each_entry(t, &m->transfers, transfer_list) - ++desc_count; - - list_for_each_entry(t, &m->transfers, transfer_list) { - ++desc_nr; - - nt = NULL; - if (desc_nr < desc_count) - nt = list_entry(t->transfer_list.next, typeof(*nt), - transfer_list); - - /* special case - send short command, read data */ - if (t->tx_buf && nt && nt->rx_buf && (t->len <= 14)) { - int len = t->len + nt->len; - u32 discard = ioread32(pdata->ioaddr + SPI_CONFIG); - - rbuf = kmalloc(nt->len, GFP_DMA); - if (!rbuf) { - pr_debug(DRIVER_NAME - ": cannot allocate memory!\n"); - return; - } - - tbuf = kmalloc(len, GFP_DMA); - if (!tbuf) { - kfree(rbuf); - pr_debug(DRIVER_NAME - ": cannot allocate memory!\n"); - return; - } - - ++desc_nr; - - /* configure hardware discard */ - discard &= ~SPI_CONFIG_DISCARD_MASK; - discard |= t->len << SPI_CONFIG_DISCARD_SHIFT; - iowrite32(discard, pdata->ioaddr + SPI_CONFIG); - - /* send cmd, then hold on idle level */ - memcpy(tbuf, t->tx_buf, t->len); - memset(tbuf + t->len, SPI_IDLE_LEVEL, t->len); - - memset(pdata->txdesc, 0, sizeof(*pdata->txdesc) * 64); - memset(pdata->rxdesc, 0, sizeof(*pdata->rxdesc) * 64); - - /* setup tx-descr. */ - map = dma_map_single(&spi->dev, (void *)tbuf, len, - DMA_TO_DEVICE); - tlen = len; - for (i = 0; tlen > 0; i++, tlen -= MAX_BUFFER_SIZE) { - pdata->txdesc[i].source = (u32)map + - (i * MAX_BUFFER_SIZE); - - if (tlen > MAX_BUFFER_SIZE) - pdata->txdesc[i].len = MAX_BUFFER_SIZE; - else - pdata->txdesc[i].len = tlen; - - pdata->txdesc[i].flags = DMADESC_FULL; - } - pdata->txdesc[i - 1].flags |= DMADESC_WRAP; - - /* rise CS after last transmit */ - if (desc_nr == desc_count || nt->cs_change) - pdata->txdesc[i - 1].flags |= DMATXDESC_LAST; - - /* setup rx-descr. */ - map = dma_map_single(&spi->dev, (void *)rbuf, len, - DMA_FROM_DEVICE); - tlen = nt->len; - for (i = 0; tlen > 0; i++, tlen -= MAX_BUFFER_SIZE) { - pdata->rxdesc[i].source = (u32)map + - (i * MAX_BUFFER_SIZE); - if (tlen > MAX_BUFFER_SIZE) - pdata->rxdesc[i].len = MAX_BUFFER_SIZE; - else - pdata->rxdesc[i].len = tlen; - } - pdata->rxdesc[i - 1].flags |= DMADESC_WRAP; - - /* gpio cs has to be toggled by driver */ - if (pdata->gpio) { - gpio_set_value(pdata->gpio->gpios[cs], 0); - } - - /* fire up dma */ - spi_irq_pending = 1; - iowrite32(DMA_IRQCFG_NRIE, - pdata->ioaddr + DMA_RXIRQCFG); - iowrite32(DMA_ENABLECHANNEL, - pdata->ioaddr + DMA_RXCTL); - iowrite32(DMA_ENABLECHANNEL, - pdata->ioaddr + DMA_TXCTL); - - /* wait for interrupt */ - if (!wait_event_timeout(pdata->waitq, - (spi_irq_pending == 0), HZ)) - dev_err(&spi->dev, "IRQ wait timed out!\n"); - - if (!(pdata->rxdesc[i - 1].flags & DMADESC_FULL)) - dev_warn(&spi->dev, "DMA descriptor broken!\n"); - - /* gpio cs has to be toggled by driver */ - if ((desc_nr == desc_count || nt->cs_change) && - pdata->gpio) { - gpio_set_value(pdata->gpio->gpios[cs], 1); - } - - /* copy rx to caller */ - memcpy(nt->rx_buf, rbuf, nt->len); - - /* cleanup */ - dma_unmap_single(&spi->dev, pdata->rxdesc[0].source, - nt->len, DMA_FROM_DEVICE); - dma_unmap_single(&spi->dev, pdata->txdesc[0].source, - len, DMA_TO_DEVICE); - - kfree(rbuf); - kfree(tbuf); - - m->actual_length += len; - t = nt; - break; - } - - rbuf = kzalloc(t->len, GFP_DMA); - if (!rbuf) { - pr_debug(DRIVER_NAME ": cannot allocate memory!\n"); - return; - } - - tbuf = kmalloc(t->len, GFP_DMA); - if (!tbuf) { - kfree(rbuf); - pr_debug(DRIVER_NAME ": cannot allocate memory!\n"); - return; - } - - if (t->tx_buf) - memcpy(tbuf, t->tx_buf, t->len); - else - /* hold dout line idle */ - memset(tbuf, SPI_IDLE_LEVEL, t->len); - - memset(pdata->txdesc, 0, sizeof(*pdata->txdesc) * 64); - memset(pdata->rxdesc, 0, sizeof(*pdata->rxdesc) * 64); - - map = dma_map_single(&spi->dev, (void *)tbuf, t->len, - DMA_TO_DEVICE); - rmap = dma_map_single(&spi->dev, (void *)rbuf, t->len, - DMA_FROM_DEVICE); - - /* setup descr. */ - tlen = t->len; - for (i = 0; tlen > 0; i++, tlen -= MAX_BUFFER_SIZE) { - pdata->txdesc[i].source = (u32)map + - (i * MAX_BUFFER_SIZE); - pdata->rxdesc[i].source = (u32)rmap + - (i * MAX_BUFFER_SIZE); - - if (tlen > MAX_BUFFER_SIZE) { - pdata->txdesc[i].len = MAX_BUFFER_SIZE; - pdata->rxdesc[i].len = MAX_BUFFER_SIZE; - } else { - pdata->txdesc[i].len = tlen; - pdata->rxdesc[i].len = tlen; - } - - pdata->txdesc[i].flags = DMADESC_FULL; - } - pdata->txdesc[i - 1].flags |= DMADESC_WRAP; - pdata->rxdesc[i - 1].flags |= DMADESC_WRAP; - - /* rise CS after last transmit */ - if (desc_nr == desc_count || nt->cs_change) - pdata->txdesc[i - 1].flags |= DMATXDESC_LAST; - - /* gpio cs has to be toggled by driver */ - if (pdata->gpio) { - gpio_set_value(pdata->gpio->gpios[cs], 0); - } - - /* fire up dma */ - spi_irq_pending = 1; - if (t->rx_buf) { - iowrite32(0, pdata->ioaddr + DMA_TXIRQCFG); - iowrite32(DMA_IRQCFG_NRIE, pdata->ioaddr + - DMA_RXIRQCFG); - iowrite32(DMA_ENABLECHANNEL, pdata->ioaddr + DMA_RXCTL); - } else - iowrite32(DMA_IRQCFG_NRIE, pdata->ioaddr + - DMA_TXIRQCFG); - iowrite32(DMA_ENABLECHANNEL, pdata->ioaddr + DMA_TXCTL); - - /* wait for interrupt */ - if (!wait_event_timeout(pdata->waitq, - (spi_irq_pending == 0), HZ)) - dev_err(&spi->dev, "IRQ wait timed out!\n"); - - if (t->rx_buf && !(pdata->rxdesc[i - 1].flags & DMADESC_FULL)) - dev_warn(&spi->dev, "DMA descriptor broken!\n"); - - /* gpio cs has to be toggled by driver */ - if ((desc_nr == desc_count || nt->cs_change) && - pdata->gpio) { - gpio_set_value(pdata->gpio->gpios[cs], 1); - } +unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; +void (* func)(struct spi_device *spi, int active) = spi->controller_data; +struct spi_ns921x *info = spi_master_get_devdata(spi->master); + + /* use special function to toggle CS? */ + if( func != NULL ) { + /* Reconfigure CS line as GPIO input */ + if( SPI_CS_ACTIVE == active ) + gpio_configure_ns921x(info->pdata->gpios[SPI_EN_GPIO_OFFSET], + NS921X_GPIO_INPUT, + NS921X_GPIO_DONT_INVERT, + NS921X_GPIO_FUNC_3, + NS921X_GPIO_ENABLE_PULLUP ); + + /* Call the special function */ + func( spi, cspol == active); + + /* Reconfigure CS line as SPI_EN */ + if( SPI_CS_INACTIVE == active ) + gpio_configure_ns921x(info->pdata->gpios[SPI_EN_GPIO_OFFSET], + 0, + 0, + info->pdata->gpio_funcs[SPI_EN_GPIO_OFFSET], + 0); + } + else { + /* Use a GPIO to toggle CS? */ + if( info->pdata->gpio_funcs[SPI_EN_GPIO_OFFSET] == NS921X_GPIO_FUNC_GPIO ) + gpio_set_value( info->pdata->gpios[SPI_EN_GPIO_OFFSET], cspol == active ); + /* otherwise CS will be automatically controlled by the spi engine */ + } +} - /* cleanup */ - dma_unmap_single(&spi->dev, pdata->txdesc[0].source, t->len, - DMA_TO_DEVICE); - dma_unmap_single(&spi->dev, pdata->rxdesc[0].source, t->len, - DMA_FROM_DEVICE); +/* + * Configure the controller for the passed SPI-device. Additionally it will + * use the configuration values of the SPI-transfer if it's different than NULL + */ +static int spi_ns921x_setupxfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct spi_ns921x *info = spi_master_get_devdata(spi->master); + unsigned int reg; + unsigned int bpw; + unsigned int hz; - /* copy rx back to caller if wanted */ - if (t->rx_buf) - memcpy(t->rx_buf, rbuf, t->len); + pr_debug("Setup for next xfer called\n"); - m->actual_length += t->len; + /* If the bits per word is zero, then use the default value of the SPI-device */ + bpw = t ? t->bits_per_word : spi->bits_per_word; + if (!bpw) + bpw = spi->bits_per_word; - kfree(rbuf); - kfree(tbuf); + /* We only support eight bits per word */ + if (bpw != 8) { + pr_err("Invalid bits-per-word (%d)\n", bpw); + return -EINVAL; } - /* finally tell spi-stack: work has finished */ - m->status = 0; - m->complete(m->context); -} + /* + * If the clock rate of the xfer is zero, then use the already configured + * rate of this SPI-device + */ + hz = t ? t->speed_hz : spi->max_speed_hz; + if (!hz) + hz = spi->max_speed_hz; -static void spi_ns921x_work(struct work_struct *work) -{ - struct spi_ns921x *pdata = container_of(work, struct spi_ns921x, work); - unsigned long flags; - - spin_lock_irqsave(&pdata->lock, flags); - while (!list_empty(&pdata->queue)) { - struct spi_message *m = container_of(pdata->queue.next, - struct spi_message, queue); + if( hz > MAX_RATE ) { + pr_err("Invalid speed (%d). Max is %d\n", hz, spi->max_speed_hz ); + return -EINVAL; + } + /* configure clock divisor (3 steps, see hardware reference) */ + /* step1: unset SPI_CLOCK_ENABLE, don't touch other bits */ + reg = ioread32(info->ioaddr + SPI_CLOCK); + iowrite32(reg & ~SPI_CLOCK_ENABLE, info->ioaddr + SPI_CLOCK); - list_del_init(&m->queue); - spin_unlock_irqrestore(&pdata->lock, flags); + /* step2: setup divior */ + reg = (clk_get_rate(info->clk) / hz + 1) & + (SPI_CLOCK_DIVISOR_MASK << SPI_CLOCK_DIVISOR_SHIFT); + iowrite32(reg, info->ioaddr + SPI_CLOCK); - spi_ns921x_work_one(pdata, m); + /* step3: set SPI_CLOCK_ENABLE, don't touch other bits */ + iowrite32(reg | SPI_CLOCK_ENABLE, info->ioaddr + SPI_CLOCK); - spin_lock_irqsave(&pdata->lock, flags); - } - spin_unlock_irqrestore(&pdata->lock, flags); + return 0; } static int spi_ns921x_setup(struct spi_device *spi) { - struct spi_ns921x *pdata = spi_master_get_devdata(spi->master); + struct spi_ns921x *info = spi_master_get_devdata(spi->master); unsigned int reg; + int ret; - spi->bits_per_word = spi->bits_per_word ? : 8; - if (spi->bits_per_word != 8) + if (spi->chip_select > spi->master->num_chipselect) { + dev_dbg(&spi->dev, + "setup: invalid chipselect %u (%u defined)\n", + spi->chip_select, spi->master->num_chipselect); return -EINVAL; + } - if (spi->max_speed_hz && (spi->max_speed_hz < MAX_RATE)) - pdata->speed = spi->max_speed_hz; - else - pdata->speed = MAX_RATE; + if (!spi->bits_per_word) + spi->bits_per_word = 8; /* configure SPI controller */ reg = SPI_CONFIG_MASTER; @@ -407,68 +274,311 @@ static int spi_ns921x_setup(struct spi_device *spi) else reg |= SPI_CONFIG_MODE0; - iowrite32(reg, pdata->ioaddr + SPI_CONFIG); + iowrite32(reg, info->ioaddr + SPI_CONFIG); - /* configure clock divisor (3 steps, see hardware reference) */ - /* step1: unset SPI_CLOCK_ENABLE, don't touch other bits */ - reg = ioread32(pdata->ioaddr + SPI_CLOCK); - iowrite32(reg & ~SPI_CLOCK_ENABLE, pdata->ioaddr + SPI_CLOCK); + ret = spi_ns921x_setupxfer(spi, NULL); + if (!ret) + pr_debug("Mode %d, %u bpw, %d HZ\n", + spi->mode, spi->bits_per_word, + spi->max_speed_hz); - /* step2: setup divior */ - reg = (clk_get_rate(pdata->clk) / pdata->speed + 1) & - (SPI_CLOCK_DIVISOR_MASK << SPI_CLOCK_DIVISOR_SHIFT); - iowrite32(reg, pdata->ioaddr + SPI_CLOCK); + return ret; +} - /* step3: set SPI_CLOCK_ENABLE, don't touch other bits */ - iowrite32(reg | SPI_CLOCK_ENABLE, pdata->ioaddr + SPI_CLOCK); +static void spi_ns921x_map_xfer(struct spi_device *spi, + struct spi_transfer *xfer) +{ + xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS; + if (!(xfer->len & (L1_CACHE_BYTES - 1))) { + if (xfer->tx_buf && !((unsigned long)xfer->tx_buf & + (L1_CACHE_BYTES - 1))) + xfer->tx_dma = dma_map_single(&spi->dev, (void *)xfer->tx_buf, + xfer->len, DMA_TO_DEVICE); + if (xfer->rx_buf && !((unsigned long)xfer->rx_buf & + (L1_CACHE_BYTES - 1))) + xfer->rx_dma = dma_map_single(&spi->dev, xfer->rx_buf, + xfer->len, DMA_FROM_DEVICE); + } +} - return 0; +static void spi_ns921x_next_xfer(struct spi_master *master, + struct spi_message *msg) +{ + struct spi_ns921x *info = spi_master_get_devdata(master); + struct spi_transfer *xfer; + dma_addr_t tx_dma, rx_dma; + u32 len; + int err; + + xfer = info->current_transfer; + if (!xfer || info->remaining_bytes == 0) { + if (xfer) + xfer = list_entry(xfer->transfer_list.next, + struct spi_transfer, transfer_list); + else + xfer = list_entry(msg->transfers.next, + struct spi_transfer, transfer_list); + info->remaining_bytes = xfer->len; + info->current_transfer = xfer; + } + + len = info->remaining_bytes; + + /* Set the requested configuration for this new SPI-transfer */ + err = spi_ns921x_setupxfer(msg->spi, xfer); + if (err) { + pr_err("Setting up the next SPI-transfer\n"); + msg->complete(msg->context); + return; + } + + tx_dma = xfer->tx_dma; + rx_dma = xfer->rx_dma; + + if (rx_dma == INVALID_DMA_ADDRESS) { + rx_dma = info->buf_rx_dma; + if (len > BUFFER_SIZE) + len = BUFFER_SIZE; + } + if (tx_dma == INVALID_DMA_ADDRESS) { + if (xfer->tx_buf) { + tx_dma = info->buf_tx_dma; + if (len > BUFFER_SIZE) + len = BUFFER_SIZE; + memcpy(info->buf_tx, xfer->tx_buf + + xfer->len - info->remaining_bytes, len); + dma_sync_single_for_device(&master->dev, + info->buf_tx_dma, len, DMA_TO_DEVICE); + } else { + /* Send undefined data; rx_dma is handy */ + tx_dma = rx_dma; + } + } + + /* Setup the DMA channels */ + /* reset dma controller */ + iowrite32(0, info->ioaddr + DMA_RXCTL); + iowrite32(0, info->ioaddr + DMA_TXCTL); + /* set DMA descriptor */ + memset(info->desc_tx, 0, sizeof(*info->desc_tx)); + info->desc_tx->source = (u32)(tx_dma); + info->desc_tx->len = len; + info->desc_tx->flags = + DMADESC_WRAP | DMADESC_INTR | DMATXDESC_LAST | DMADESC_FULL; + + memset(info->desc_rx, 0, sizeof(*info->desc_rx)); + info->desc_rx->source = (u32)(rx_dma); + info->desc_rx->len = len; + info->desc_rx->flags = DMADESC_WRAP; + + info->dma_xfer_len = len; + + /* configure dma channels */ + iowrite32( DMA_IRQCFG_NCIE | DMA_IRQCFG_ECIE | + DMA_IRQCFG_NRIE | DMA_IRQCFG_CAIE | DMA_IRQCFG_PCIE, + info->ioaddr + DMA_RXIRQCFG ); + iowrite32((u32)info->desc_tx_dma, info->ioaddr + DMA_TXBUF); + iowrite32( DMA_IRQCFG_NCIE | DMA_IRQCFG_ECIE | + DMA_IRQCFG_NRIE | DMA_IRQCFG_CAIE, + info->ioaddr + DMA_TXIRQCFG ); + iowrite32((u32)info->desc_rx_dma, info->ioaddr + DMA_RXBUF); + + /* fire up dma channels */ + iowrite32(DMA_ENABLECHANNEL, info->ioaddr + DMA_RXCTL); + iowrite32(DMA_ENABLECHANNEL, info->ioaddr + DMA_TXCTL); } -static int spi_ns921x_transfer(struct spi_device *spi, struct spi_message *m) +static void spi_ns921x_next_message(struct spi_master *master) { - struct spi_ns921x *pdata = spi_master_get_devdata(spi->master); - struct spi_transfer *t; + struct spi_ns921x *info = spi_master_get_devdata(master); + struct spi_message *msg; + + BUG_ON(info->current_transfer); + + msg = list_entry(info->queue.next, struct spi_message, queue); + + spi_ns921x_chipsel(msg->spi, SPI_CS_ACTIVE); + spi_ns921x_next_xfer(master, msg); +} + +static int spi_ns921x_transfer(struct spi_device *spi, struct spi_message *msg) +{ + struct spi_ns921x *info = spi_master_get_devdata(spi->master); + struct spi_transfer *xfer; unsigned long flags; + if (unlikely(list_empty(&msg->transfers) + || !spi->max_speed_hz)) + return -EINVAL; - list_for_each_entry(t, &m->transfers, transfer_list) { - if (!t->tx_buf && !t->rx_buf) - return -EINVAL; - t->bits_per_word = t->bits_per_word ? : 8; - if (t->bits_per_word != 8) - return -EINVAL; - if (t->len & ((t->bits_per_word >> 3) - 1)) - return -EINVAL; - if (t->speed_hz > MAX_RATE) + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (!(xfer->tx_buf || xfer->rx_buf)) { + dev_dbg(&spi->dev, "missing rx or tx buf\n"); return -EINVAL; + } } - spin_lock_irqsave(&pdata->lock, flags); - list_add_tail(&m->queue, &pdata->queue); - queue_work(pdata->workqueue, &pdata->work); - spin_unlock_irqrestore(&pdata->lock, flags); + /* scrub dcache "early" */ + if (!msg->is_dma_mapped) { + list_for_each_entry(xfer, &msg->transfers, transfer_list) + spi_ns921x_map_xfer(spi, xfer); + } + + msg->status = -EINPROGRESS; + msg->actual_length = 0; + + spin_lock_irqsave(&info->lock, flags); + list_add_tail(&msg->queue, &info->queue); + if (!info->current_transfer) + spi_ns921x_next_message(spi->master); + spin_unlock_irqrestore(&info->lock, flags); return 0; } -static irqreturn_t spi_ns921x_irqhandler(int irq, void *dev_id) +static void spi_ns921x_irq_trigger_next(struct spi_master *master) { - struct spi_ns921x *pdata = dev_id; - u32 status = ioread32(pdata->ioaddr + DMA_IRQSTATUS); + struct spi_ns921x *info = spi_master_get_devdata(master); + struct spi_message *msg; + struct spi_transfer *xfer; + u32 len, ctrl; + + BUG_ON((info->flags & (SPI_TX_IDONE|SPI_RX_IDONE)) != (SPI_TX_IDONE|SPI_RX_IDONE)); + + info->flags &= ~(SPI_TX_IDONE|SPI_RX_IDONE); + xfer = info->current_transfer; + msg = list_entry(info->queue.next, struct spi_message, queue); + + len = info->dma_xfer_len; + if (len > BUFFER_SIZE) + len = BUFFER_SIZE; + /* + * If the rx buffer wasn't aligned, we used a bounce + * buffer for the transfer. Copy the data back and + * make the bounce buffer ready for re-use. + */ + if (xfer->rx_buf && xfer->rx_dma == INVALID_DMA_ADDRESS) { + dma_sync_single_for_cpu(&master->dev, info->buf_rx_dma, + len, DMA_FROM_DEVICE); + memcpy(xfer->rx_buf, info->buf_rx + xfer->len - + info->remaining_bytes, len); + } + + info->remaining_bytes -= len; + + if (info->remaining_bytes == 0) { + msg->actual_length += xfer->len; + + if (!msg->is_dma_mapped) { + if (xfer->tx_dma != INVALID_DMA_ADDRESS) + dma_unmap_single(&master->dev, + xfer->tx_dma, + xfer->len, + DMA_TO_DEVICE); + if (xfer->rx_dma != INVALID_DMA_ADDRESS) + dma_unmap_single(&master->dev, + xfer->rx_dma, + xfer->len, + DMA_FROM_DEVICE); + } - /* ack irq */ - iowrite32(status, pdata->ioaddr + DMA_IRQSTATUS); + /* REVISIT: udelay in irq is unfriendly */ + if (xfer->delay_usecs) + udelay(xfer->delay_usecs); + + if (msg->transfers.prev == &xfer->transfer_list) { + + /* Message complete */ + spi_ns921x_chipsel(msg->spi, SPI_CS_INACTIVE); + + list_del(&msg->queue); + msg->status = 0; + + spin_unlock(&info->lock); + msg->complete(msg->context); + spin_lock(&info->lock); + + info->current_transfer = NULL; + + /* continue; complete() may have queued requests */ + if (list_empty(&info->queue)) { + ctrl = ioread32(info->ioaddr + + DMA_RXCTL); + ctrl &= ~DMA_ENABLECHANNEL; + iowrite32(ctrl, info->ioaddr + + DMA_RXCTL); + ctrl = ioread32(info->ioaddr + + DMA_TXCTL); + ctrl &= ~DMA_ENABLECHANNEL; + iowrite32(ctrl, info->ioaddr + + DMA_TXCTL); + } else + spi_ns921x_next_message(master); + } else { + if (xfer->cs_change) { + spi_ns921x_chipsel(msg->spi, SPI_CS_INACTIVE); + udelay(1); + spi_ns921x_chipsel(msg->spi, SPI_CS_ACTIVE); + } - if (status & (SPI_IRQSTATUS_RXNRIP | SPI_IRQSTATUS_TXNRIP)) { - spi_irq_pending = 0; - wake_up(&pdata->waitq); - } else - /* not from our hardware - * should NOT happen, as this is not a shared IRQ*/ - return IRQ_NONE; + spi_ns921x_next_xfer(master, msg); + } + } else { + /* There are still pending data in the current transfer */ + spi_ns921x_next_xfer(master, msg); + } +} - iowrite32(0, pdata->ioaddr + DMA_RXCTL); - iowrite32(0, pdata->ioaddr + DMA_TXCTL); +static irqreturn_t spi_ns921x_irq(int irq, void *dev_id) +{ + struct spi_master *master = dev_id; + struct spi_ns921x *info = spi_master_get_devdata(master); + u32 status, dlen, rx_status; + + spin_lock(&info->lock); + + /* get status and mask all pending interrupts */ + status = ioread32(info->ioaddr + DMA_IRQSTATUS); + iowrite32(status, info->ioaddr + DMA_IRQSTATUS); + + /* TX interrupt error conditions */ + if (status & (DMA_IRQSTAT_TXECIP | DMA_IRQSTAT_TXCAIP )) { + dev_err(&master->dev, "tx dma failure! status = 0x%08x\n", + status); + /* retrigger the xfer */ + spi_ns921x_next_xfer(master, list_entry(info->queue.next, + struct spi_message, queue)); + } + /* RX interrupt error conditions */ + rx_status = ioread32(info->ioaddr + DMA_RXIRQCFG); + dlen = rx_status & DMA_IRQCFG_BLENSTAT; + if (info->dma_xfer_len != dlen) + /* ?? what to do... ?? */ + dev_err(&master->dev, "incomplete dma xfer" + "(%d/%d bytes transfered) \n", + dlen, info->dma_xfer_len); + + if (status & (DMA_IRQSTAT_RXECIP | DMA_IRQSTAT_RXCAIP | + DMA_IRQSTAT_RXPCIP)) { + dev_err(&master->dev, "rx dma failure! status = 0x%08x\n", + status); + + /* retrigger the xfer */ + spi_ns921x_next_xfer(master, list_entry(info->queue.next, + struct spi_message, queue)); + } + /* TX normal completion */ + if (status & DMA_IRQSTAT_TXNCIP) { + info->flags |= SPI_TX_IDONE; + if (info->flags & SPI_RX_IDONE) + spi_ns921x_irq_trigger_next(master); + } + /* RX normal completion */ + if (status & DMA_IRQSTAT_RXNCIP) { + info->flags |= SPI_RX_IDONE; + if (info->flags & SPI_TX_IDONE) + spi_ns921x_irq_trigger_next(master); + } + spin_unlock(&info->lock); return IRQ_HANDLED; } @@ -476,128 +586,129 @@ static irqreturn_t spi_ns921x_irqhandler(int irq, void *dev_id) static __devinit int spi_ns921x_probe(struct platform_device *pdev) { struct spi_master *master; - struct spi_ns921x *pdata; - int ret, i; + struct spi_ns921x *info; + int ret; + int gpio_cs, gpio_cs_func; - master = spi_alloc_master(&pdev->dev, sizeof(*pdata)); + master = spi_alloc_master(&pdev->dev, sizeof(*info)); if (!master) { pr_debug(DRIVER_NAME ": cannot allocate spi master\n"); return -ENOMEM; } - pdata = spi_master_get_devdata(master); platform_set_drvdata(pdev, master); - if (pdev->dev.platform_data) { - pdata->gpio = pdev->dev.platform_data; - for(i = 0; i < pdata->gpio->nr_gpios; i++) { - ret = gpio_request(pdata->gpio->gpios[i], DRIVER_NAME); - if (ret) { - for(i--; i >= 0; i++) - gpio_free(pdata->gpio->gpios[i]); - goto err_gpio; - } - gpio_direction_output(pdata->gpio->gpios[i], 1); - } - } else - pdata->gpio = NULL; - - /* prepare waitq and spinlock */ - INIT_WORK(&pdata->work, spi_ns921x_work); - spin_lock_init(&pdata->lock); - INIT_LIST_HEAD(&pdata->queue); - init_waitqueue_head(&pdata->waitq); - - pdata->irq = platform_get_irq(pdev, 0); - if (pdata->irq < 0) { + info = spi_master_get_devdata(master); + info->pdata = pdev->dev.platform_data; + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) { pr_debug(DRIVER_NAME ": no interrupt available\n"); ret = -ENOENT; goto err_res; } - pdata->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!pdata->mem) { + info->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!info->mem) { pr_debug(DRIVER_NAME ": memory not available\n"); ret = -ENOENT; goto err_res; } - if (!request_mem_region(pdata->mem->start, - pdata->mem->end - pdata->mem->start + 1, + if (!request_mem_region(info->mem->start, + info->mem->end - info->mem->start + 1, DRIVER_NAME)) { pr_debug(DRIVER_NAME ": memory already mapped\n"); ret = -EIO; goto err_res; } - pdata->ioaddr = ioremap(pdata->mem->start, - pdata->mem->end - pdata->mem->start + 1); - if (!pdata->ioaddr) { + info->ioaddr = ioremap(info->mem->start, + info->mem->end - info->mem->start + 1); + if (!info->ioaddr) { pr_debug(DRIVER_NAME ": unable to remap IO memory\n"); ret = -EIO; goto err_remap; } - pr_debug(DRIVER_NAME ": mapped SPI controller to address 0x%x\n", - (int)pdata->ioaddr); + + master->bus_num = pdev->id; + /* hardware controlled cs */ + master->num_chipselect = 1; + + /* If CS is GPIO controlled, configure it as output */ + gpio_cs = info->pdata->gpios[SPI_EN_GPIO_OFFSET]; + gpio_cs_func = info->pdata->gpio_funcs[SPI_EN_GPIO_OFFSET]; + if( NS921X_GPIO_FUNC_GPIO == gpio_cs_func ) + gpio_direction_output( gpio_cs, + gpio_get_value( gpio_cs ) ); + + master->setup = spi_ns921x_setup; + master->transfer = spi_ns921x_transfer; + + /* prepare waitq and spinlock */ + spin_lock_init(&info->lock); + INIT_LIST_HEAD(&info->queue); + init_waitqueue_head(&info->waitq); /* register and map memory for dma-descriptors */ - pdev->dev.coherent_dma_mask = (u32)-1; - pdata->rxdesc = dma_alloc_coherent(&pdev->dev, - sizeof(pdata->rxdesc) * 64, - &pdata->rxdesc_real, GFP_KERNEL); - pdata->txdesc = dma_alloc_coherent(&pdev->dev, - sizeof(pdata->txdesc) * 64, - &pdata->txdesc_real, GFP_KERNEL); - if (!pdata->rxdesc || !pdata->txdesc) { - pr_debug(DRIVER_NAME - ": cannot allocate dma transfer memory\n"); + info->buf_tx = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE, + &info->buf_tx_dma, GFP_KERNEL); + if (!info->buf_tx) { ret = -ENOMEM; - goto err_dma; + dev_dbg(&pdev->dev, "%s: err_alloc_buf_tx\n", __func__); + goto err_alloc_buf_tx; } - iowrite32((u32)pdata->rxdesc_real, pdata->ioaddr + DMA_RXBUF); - iowrite32((u32)pdata->txdesc_real, pdata->ioaddr + DMA_TXBUF); - pdata->clk = clk_get(&pdev->dev, DRIVER_NAME); - if (IS_ERR(pdata->clk)) { + info->buf_rx = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE, + &info->buf_rx_dma, GFP_KERNEL); + if (!info->buf_rx) { + ret = -ENOMEM; + dev_dbg(&pdev->dev, "%s: err_alloc_buf_rx\n", __func__); + goto err_alloc_buf_rx; + } + + info->desc_tx = dma_alloc_coherent(&pdev->dev, + sizeof(info->desc_tx), &info->desc_tx_dma, GFP_DMA); + if (!info->desc_tx) { + ret = -ENOMEM; + dev_dbg(&pdev->dev, "%s: err_alloc_desc_tx\n", __func__); + goto err_alloc_desc_tx; + } + + info->desc_rx = dma_alloc_coherent(&pdev->dev, + sizeof(info->desc_rx), &info->desc_rx_dma, GFP_DMA); + if (!info->desc_rx) { + ret = -ENOMEM; + dev_dbg(&pdev->dev, "%s: err_alloc_desc_rx\n", __func__); + goto err_alloc_desc_rx; + } + + info->clk = clk_get(&pdev->dev, DRIVER_NAME); + if (IS_ERR(info->clk)) { pr_debug(DRIVER_NAME ": no clock available\n"); - ret = PTR_ERR(pdata->clk); + ret = PTR_ERR(info->clk); goto err_clk; } - ret = clk_enable(pdata->clk); + ret = clk_enable(info->clk); if (ret) { pr_debug(DRIVER_NAME ": cannot enable clock\n"); goto err_clk; } /* reset dma controller */ - iowrite32(0, pdata->ioaddr + DMA_RXCTL); - iowrite32(0, pdata->ioaddr + DMA_TXCTL); + iowrite32(0, info->ioaddr + DMA_RXCTL); + iowrite32(0, info->ioaddr + DMA_TXCTL); /* register interrupt service routine */ - ret = request_irq(pdata->irq, spi_ns921x_irqhandler, 0, - DRIVER_NAME, pdata); + ret = request_irq(info->irq, spi_ns921x_irq, 0, + pdev->name, master); if (ret) { pr_debug(DRIVER_NAME ": cannot register SPI interrupt\n"); goto err_irq; } - /* create workqueue for transmits */ - pdata->workqueue = - create_singlethread_workqueue(master->dev.parent->bus_id); - if (!pdata->workqueue) { - pr_debug(DRIVER_NAME ": cannot create workqueue thread\n"); - goto err_workqueue; - } - - /* register spi master device */ - master->bus_num = pdev->id; - master->setup = spi_ns921x_setup; - master->transfer = spi_ns921x_transfer; - if (pdata->gpio) - master->num_chipselect = pdata->gpio->nr_gpios; - else - master->num_chipselect = 1; + dev_info(&pdev->dev, "NS921x SPI controller at 0x%p (irq: %d)\n", + info->ioaddr, info->irq ); ret = spi_register_master(master); if (ret) { @@ -608,28 +719,30 @@ static __devinit int spi_ns921x_probe(struct platform_device *pdev) return 0; err_reg: - destroy_workqueue(pdata->workqueue); -err_workqueue: - free_irq(pdata->irq, pdata); + //destroy_workqueue(info->workqueue); +//err_workqueue: + free_irq(info->irq, info); err_irq: - clk_disable(pdata->clk); + clk_disable(info->clk); err_clk: -err_dma: - if (pdata->rxdesc) - dma_free_coherent(&pdev->dev, sizeof(pdata->rxdesc) * 64, - pdata->rxdesc, pdata->rxdesc_real); - if (pdata->txdesc) - dma_free_coherent(&pdev->dev, sizeof(pdata->txdesc) * 64, - pdata->txdesc, pdata->txdesc_real); - iounmap(pdata->ioaddr); + dma_free_coherent(&pdev->dev, sizeof(info->desc_rx), + info->desc_rx, info->desc_rx_dma); +err_alloc_desc_rx: + dma_free_coherent(&pdev->dev, sizeof(info->desc_tx), + info->desc_tx, info->desc_tx_dma); +err_alloc_desc_tx: + dma_free_coherent(&pdev->dev, BUFFER_SIZE, + info->buf_rx, info->buf_rx_dma); +err_alloc_buf_rx: + dma_free_coherent(&pdev->dev, BUFFER_SIZE, + info->buf_tx, info->buf_tx_dma); +err_alloc_buf_tx: + iounmap(info->ioaddr); + err_remap: - release_mem_region(pdata->mem->start, - pdata->mem->end - pdata->mem->start + 1); + release_mem_region(info->mem->start, + info->mem->end - info->mem->start + 1); err_res: - if (pdata->gpio) - for(i = 0; i < pdata->gpio->nr_gpios; i++) - gpio_free(pdata->gpio->gpios[i]); -err_gpio: spi_master_put(master); return ret; @@ -638,26 +751,30 @@ err_gpio: static __devexit int spi_ns921x_remove(struct platform_device *pdev) { struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); - struct spi_ns921x *pdata = spi_master_get_devdata(master); + struct spi_ns921x *info = spi_master_get_devdata(master); spi_unregister_master(master); - destroy_workqueue(pdata->workqueue); - free_irq(pdata->irq, pdata); - clk_disable(pdata->clk); - dma_free_coherent(&pdev->dev, sizeof(pdata->rxdesc) * 64, - pdata->rxdesc, pdata->rxdesc_real); - dma_free_coherent(&pdev->dev, sizeof(pdata->txdesc) * 64, - pdata->txdesc, pdata->txdesc_real); - iounmap(pdata->ioaddr); - release_mem_region(pdata->mem->start, - pdata->mem->end - pdata->mem->start + 1); +// destroy_workqueue(info->workqueue); + free_irq(info->irq, info); + clk_disable(info->clk); + dma_free_coherent(&pdev->dev, sizeof(info->desc_rx), + info->desc_rx, info->desc_rx_dma); + dma_free_coherent(&pdev->dev, sizeof(info->desc_tx), + info->desc_tx, info->desc_tx_dma); + dma_free_coherent(&pdev->dev, BUFFER_SIZE, + info->buf_rx, info->buf_rx_dma); + dma_free_coherent(&pdev->dev, BUFFER_SIZE, + info->buf_tx, info->buf_tx_dma); + iounmap(info->ioaddr); + release_mem_region(info->mem->start, + info->mem->end - info->mem->start + 1); spi_master_put(master); return 0; } static struct platform_driver spi_ns9xxx_driver = { - .driver = { + .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, }, @@ -678,7 +795,7 @@ static __exit void spi_ns921x_exit(void) module_init(spi_ns921x_init); module_exit(spi_ns921x_exit); -MODULE_AUTHOR("Matthias Ludwig"); +MODULE_AUTHOR("Digi International Inc."); MODULE_DESCRIPTION("Digi ns921x SPI Controller driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/spi/spi_ns9360.c b/drivers/spi/spi_ns9360.c index 59c149edbd2827a75daf84fc22d1110ef250a85b..e137d605c0ab955ac358db3fb27d9da3427b3cb0 100644 --- a/drivers/spi/spi_ns9360.c +++ b/drivers/spi/spi_ns9360.c @@ -18,11 +18,14 @@ #include #include #include -#include #include #include #include +#include +#include + +#define DRIVER_NAME "spi_ns9360" union ns9xxx_dma_desc { struct { @@ -47,7 +50,7 @@ struct spi_ns9360 { struct resource *mem; struct resource *mem_bbus; struct clk *clk; - struct ns9360_data *pdata; + struct spi_ns9xxx_data *pdata; unsigned irq_tx; unsigned irq_rx; @@ -130,50 +133,78 @@ struct spi_ns9360 { static void spi_ns9360_chipsel(struct spi_device *spi, int active) { - struct spi_ns9360 *info = spi_master_get_devdata(spi->master); - unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; - - /* use GPIOs as CS? */ - if (info->pdata) - gpio_set_value(info->pdata->gpios[spi->chip_select], - cspol == active); - - /* otherwise CS will be automatically controlled by the spi engine */ +unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; +void (* func)(struct spi_device *spi, int active) = spi->controller_data; +struct spi_ns9360 *info = spi_master_get_devdata(spi->master); + + /* use special function to toggle CS? */ + if( func ) + { + /* Reconfigure CS line as GPIO input */ + if( SPI_CS_ACTIVE == active ) + gpio_configure_ns9360(info->pdata->gpios[SPI_EN_GPIO_OFFSET], + NS9360_GPIO_INPUT, + NS9360_GPIO_DONT_INVERT, + NS9360_GPIO_FUNC_3); + + /* Call the special function */ + func( spi, cspol == active); + + /* Reconfigure CS line as SPI_EN */ + if( SPI_CS_INACTIVE == active ) + gpio_configure_ns9360(info->pdata->gpios[SPI_EN_GPIO_OFFSET], + 0, + 0, + info->pdata->gpio_funcs[SPI_EN_GPIO_OFFSET]); + } + else { + /* Use a GPIO to toggle CS? */ + if( info->pdata->gpio_funcs[SPI_EN_GPIO_OFFSET] == NS9360_GPIO_FUNC_GPIO ) + gpio_set_value( info->pdata->gpios[SPI_EN_GPIO_OFFSET], cspol == active ); + /* otherwise CS will be automatically controlled by the spi engine */ + } } -static int spi_ns9360_setup(struct spi_device *spi) +/* + * Configure the controller for the passed SPI-device. Additionally it will + * use the configuration values of the SPI-transfer if it's different than NULL + */ +static int spi_ns9360_setupxfer(struct spi_device *spi, + struct spi_transfer *t) { struct spi_ns9360 *info = spi_master_get_devdata(spi->master); - unsigned hz; - u32 ctrl, brate; + unsigned int bpw; + unsigned int hz; + u32 brate; + u32 ctrl; - if (spi->chip_select > spi->master->num_chipselect) { - dev_dbg(&spi->dev, - "setup: invalid chipselect %u (%u defined)\n", - spi->chip_select, spi->master->num_chipselect); - return -EINVAL; - } + pr_debug("Setup for next xfer called\n"); - if (!spi->bits_per_word) - spi->bits_per_word = 8; - if (spi->bits_per_word != 8) - return -EINVAL; + /* If the bits per word is zero, then use the default value of the SPI-device */ + bpw = t ? t->bits_per_word : spi->bits_per_word; + if (!bpw) + bpw = spi->bits_per_word; - if ((spi->mode & SPI_CS_HIGH) && !info->pdata) { - /* FIXME: switch inverter on CS-pin */ + /* We only support eight bits per word */ + if (bpw != 8) { + pr_err("Invalid bits-per-word (%d)\n", bpw); + return -EINVAL; } - if (spi->max_speed_hz) - if (spi->max_speed_hz > (clk_get_rate(info->clk) / 8)) - hz = clk_get_rate(info->clk) / 8; - else - hz = spi->max_speed_hz; - else { - hz = SPI_DEFAULT_BPS; - dev_warn(&spi->dev, "max_speed_hz not provided, " - "using default %d\n", SPI_DEFAULT_BPS); + /* + * If the clock rate of the xfer is zero, then use the already configured + * rate of this SPI-device + */ + if(!spi->max_speed_hz) + spi->max_speed_hz = SPI_DEFAULT_BPS; + hz = t ? t->speed_hz : spi->max_speed_hz; + if (!hz) + hz = spi->max_speed_hz; + + if( hz > (clk_get_rate(info->clk) / 4) ) { + pr_err("Invalid speed (%d)\n", hz ); + return -EINVAL; } - /* reset previous configuration */ iowrite32(0x00, info->ioaddr + SPI_CTRLA); @@ -211,6 +242,31 @@ static int spi_ns9360_setup(struct spi_device *spi) return 0; } +static int spi_ns9360_setup(struct spi_device *spi) +{ + struct spi_ns9360 *info = spi_master_get_devdata(spi->master); + int ret; + + if (spi->chip_select > spi->master->num_chipselect) { + dev_dbg(&spi->dev, + "setup: invalid chipselect %u (%u defined)\n", + spi->chip_select, spi->master->num_chipselect); + return -EINVAL; + } + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + if ((spi->mode & SPI_CS_HIGH) && !info->pdata) { + /* FIXME: switch inverter on CS-pin */ + } + + /* Configure per-transfer settings (speed/bpw/mode) */ + ret = spi_ns9360_setupxfer(spi, NULL); + + return ret; +} + static void spi_ns9360_map_xfer(struct spi_device *spi, struct spi_transfer *xfer) { @@ -234,6 +290,7 @@ static void spi_ns9360_next_xfer(struct spi_master *master, struct spi_transfer *xfer; dma_addr_t tx_dma, rx_dma; u32 len, ctrl; + int err; xfer = info->current_transfer; if (!xfer || info->remaining_bytes == 0) { @@ -249,6 +306,14 @@ static void spi_ns9360_next_xfer(struct spi_master *master, len = info->remaining_bytes; + /* Set the requested configuration for this new SPI-transfer */ + err = spi_ns9360_setupxfer(msg->spi, xfer); + if (err) { + pr_err("Setting up the next SPI-transfer\n"); + msg->complete(msg->context); + return; + } + tx_dma = xfer->tx_dma; rx_dma = xfer->rx_dma; @@ -331,12 +396,6 @@ static int spi_ns9360_transfer(struct spi_device *spi, struct spi_message *msg) dev_dbg(&spi->dev, "missing rx or tx buf\n"); return -EINVAL; } - - /* FIXME implement these protocol options!! */ - if (xfer->bits_per_word || xfer->speed_hz) { - dev_dbg(&spi->dev, "no protocol options yet\n"); - return -ENOPROTOOPT; - } } /* scrub dcache "early" */ @@ -529,6 +588,7 @@ static int __devinit spi_ns9360_probe(struct platform_device *pdev) int ret; struct spi_master *master; struct spi_ns9360 *info; + int gpio_cs, gpio_cs_func; master = spi_alloc_master(&pdev->dev, sizeof(*info)); if (!master) { @@ -603,12 +663,16 @@ static int __devinit spi_ns9360_probe(struct platform_device *pdev) } master->bus_num = pdev->id; - if (info->pdata) - /* toggle gpio(s) as cs */ - master->num_chipselect = info->pdata->nr_gpios; - else - /* hardware controlled cs */ - master->num_chipselect = 1; + /* hardware controlled cs */ + master->num_chipselect = 1; + + /* If CS is GPIO controlled, configure it as output */ + gpio_cs = info->pdata->gpios[SPI_EN_GPIO_OFFSET]; + gpio_cs_func = info->pdata->gpio_funcs[SPI_EN_GPIO_OFFSET]; + if( NS9360_GPIO_FUNC_GPIO == gpio_cs_func ) + gpio_direction_output( gpio_cs, + gpio_get_value( gpio_cs ) ); + master->setup = spi_ns9360_setup; master->transfer = spi_ns9360_transfer; diff --git a/include/asm-arm/arch-ns9xxx/gpio.h b/include/asm-arm/arch-ns9xxx/gpio.h index dd2a99499a42753b57ee90f54eeb0cf779325600..b9f4619e149bf34ee2b109ec61bdbd85398f37ff 100644 --- a/include/asm-arm/arch-ns9xxx/gpio.h +++ b/include/asm-arm/arch-ns9xxx/gpio.h @@ -29,6 +29,8 @@ # define NS921X_GPIO_FUNC_3 (0x03) # define NS921X_GPIO_FUNC_4 (0x04) +# define NS921X_GPIO_FUNC_GPIO NS921X_GPIO_FUNC_3 + # define NS921X_GPIO_INPUT (0x00) # define NS921X_GPIO_OUTPUT (0x01) @@ -39,6 +41,23 @@ # define NS921X_GPIO_DISABLE_PULLUP (0x01) #endif +/* Macros for configuring the GPIO-functions */ +#if defined(CONFIG_PROCESSOR_NS9360) +# define NS9360_GPIO_FUNC_0 (0x00) +# define NS9360_GPIO_FUNC_1 (0x01) +# define NS9360_GPIO_FUNC_2 (0x02) +# define NS9360_GPIO_FUNC_3 (0x03) +# define NS9360_GPIO_FUNC_4 (0x04) + +# define NS9360_GPIO_FUNC_GPIO NS9360_GPIO_FUNC_3 + +# define NS9360_GPIO_INPUT (0x00) +# define NS9360_GPIO_OUTPUT (0x01) + +# define NS9360_GPIO_INVERT (0x01) +# define NS9360_GPIO_DONT_INVERT (0x00) +#endif + struct gpio_to_irq_map { unsigned gpio; diff --git a/include/asm-arm/arch-ns9xxx/spi.h b/include/asm-arm/arch-ns9xxx/spi.h new file mode 100644 index 0000000000000000000000000000000000000000..7818799166bed39c24e7fd33cc645b8f490aff7e --- /dev/null +++ b/include/asm-arm/arch-ns9xxx/spi.h @@ -0,0 +1,28 @@ +/* + * include/asm-arm/arch-ns9xxx/spi.h + * + * Copyright (C) 2008 by Digi International Inc. + * All rights reserved. + * + * 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. +*/ +#ifndef __ASM_ARCH_SPI_H +#define __ASM_ARCH_SPI_H + +#define SPI_MOSI_GPIO_OFFSET 0 +#define SPI_MISO_GPIO_OFFSET 1 +#define SPI_CLK_GPIO_OFFSET 2 +#define SPI_EN_GPIO_OFFSET 3 + + +#define SPI_MAX_GPIO 10 + +struct spi_ns9xxx_data { + char gpios[SPI_MAX_GPIO]; + char gpio_funcs[SPI_MAX_GPIO]; + char nr_gpios; +}; + +#endif /* ifndef __ASM_ARCH_SPI_H */ diff --git a/include/linux/spi_ns921x.h b/include/linux/spi_ns921x.h deleted file mode 100644 index 2d23f84decff5d343cfcb468e0758f81ef1e21a6..0000000000000000000000000000000000000000 --- a/include/linux/spi_ns921x.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * include/linux/spi_ns921x.h - * - * Copyright (C) 2008 by Digi International Inc. - * All rights reserved. - * - * 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. - */ - -struct ns921x_data { - unsigned int *gpios; - unsigned int nr_gpios; -};