/* * InHand SSP platform driver * * Copyright: 2007-2008 Inhand Electronics, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include MODULE_AUTHOR("Vernon Sauder "); MODULE_DESCRIPTION("InHand SSP platform driver"); MODULE_LICENSE("GPL"); #define MHZ *1000*1000 #define KHZ *1000 #define SSP_REG_LEN 0x40 // GPIO pins to initialize for SSP ports 1 & 2 // FT4 does not have port 3 static u32 ssp_gpio_init_list[] __initdata = { #ifdef CONFIG_SSP1_EN // pins needed for SSP1 (23 | GPIO_ALT_FN_2_OUT), // CLK //(24 | GPIO_ALT_FN_2_OUT), // Frame (25 | GPIO_ALT_FN_2_OUT), // TX (26 | GPIO_ALT_FN_1_IN), // RX // don't enable EXTCLK unless you actually are using it. Otherwise, // the SSPCLK will always run (for some strange reason) //(27 | GPIO_ALT_FN_2_IN), // EXTCLK #endif #ifdef CONFIG_SSP2_EN // pins needed for SSP2 (36 | GPIO_ALT_FN_2_OUT), // CLK (37 | GPIO_ALT_FN_2_OUT), // Frame (13 | GPIO_ALT_FN_1_OUT), // TX (29 | GPIO_ALT_FN_1_OUT), // RX (yes, out) // don't enable EXTCLK unless you actually are using it. Otherwise, // the SSPCLK will always run (for some strange reason) //(22 | GPIO_ALT_FN_1_IN), // EXTCLK #endif }; // SPI Chips #ifdef CONFIG_SSP1_EN // enable as needed //#define M25P16 // SPI FLash #define SD // driver for SD card in SPI mode // use FRM as CS0 static gpreg ssp1_cs0_gpio = { .name = "SSP1 CS0", .offset = 24, .output = 1, .act_low = 1, .drv = &pxa_gpreg_drv, }; // use EXTCLK as CS1 static gpreg ssp1_cs1_gpio = { .name = "SSP1 CS1", .offset = 27, .output = 1, .act_low = 1, .drv = &pxa_gpreg_drv, }; // SPI driver callback to manipulate CS0 static void ssp_cs0_cb(u32 command) { if (command == PXA2XX_CS_ASSERT) gpreg_activate(&ssp1_cs0_gpio); else gpreg_deactivate(&ssp1_cs0_gpio); } // SPI driver callback to manipulate CS1 static void ssp_cs1_cb(u32 command) { if (command == PXA2XX_CS_ASSERT) gpreg_activate(&ssp1_cs1_gpio); else gpreg_deactivate(&ssp1_cs1_gpio); } /* SPI chip data */ static struct pxa2xx_spi_chip ssp1_cs0_settings = { .tx_threshold = 12, .rx_threshold = 4, .cs_control = ssp_cs0_cb, }; static struct pxa2xx_spi_chip ssp1_cs1_settings = { .cs_control = ssp_cs1_cb, }; #ifdef M25P16 // declare the type of SPI flash and MTD config info static struct flash_platform_data pdata_m25p16 = { .name = "spi", .type = "m25p16", }; #endif // The set of devices that are on the SPI (1) bus. static struct spi_board_info spi1_chips[] __initdata = { // This setup is invalid because they all use CS0. If this were a real // setup, they would all use different controller_data's. For this example, // it allows each device to be used by physically connecting and // disconnecting (and insmod/rmmod) as needed for testing. // 'spidev' is a user-mode interface. It can theoritically be used alongside // a kernel-mode driver as long as they are not used at the same time. The // spidev interface allows user-mode debugging of the kernel-mode driver. // If a real user-mode driver was used, the kernel driver would need to be disabled. // The .chip_select field is better called device-index. { .modalias = "spidev", //.platform_data = 0, .controller_data = &ssp1_cs0_settings, //.irq = DEV_IRQ, //.mode = SPI_CPOL | SPI_CPHA, .max_speed_hz = 10 MHZ, // max - PXA only does 13MHz .bus_num = 1, // SSP1 .chip_select = 0, // device number on bus (0-based) }, { .modalias = "spidev", .controller_data = &ssp1_cs1_settings, .max_speed_hz = 10 MHZ, // max - PXA only does 13MHz .bus_num = 1, // SSP1 .chip_select = 1, // device number on bus (0-based) }, #ifdef M25P16 { // SPI FLash .modalias = "m25p80", .platform_data = &pdata_m25p16, .controller_data = &ssp1_cs0_settings, .max_speed_hz = 20 MHZ, .bus_num = 1, .chip_select = 2, // device number on bus (0-based) }, #endif #ifdef SD { .modalias = "mmc_spi", .controller_data = &ssp1_cs0_settings, .max_speed_hz = 20 MHZ, .bus_num = 1, #ifdef M25P16 .chip_select = 3, // device number on bus (0-based) #else .chip_select = 2, // device number on bus (0-based) #endif }, #endif }; static struct resource pxa_ssp1_resources[] = { { .start = __PREG(SSCR0_P(1)), /* Start address of SSP1 registers */ .end = __PREG(SSCR0_P(1)) + SSP_REG_LEN - 1, .flags = IORESOURCE_MEM, }, { .start = IRQ_SSP, .end = IRQ_SSP, .flags = IORESOURCE_IRQ, }, }; static struct pxa2xx_spi_master pxa_ssp1_master_info = { .ssp_type = PXA27x_SSP, .clock_enable = CKEN_SSP1, .num_chipselect = ARRAY_SIZE(spi1_chips), /* number of chips attached to SSP */ .enable_dma = 1, }; static struct platform_device pxa_ssp1 = { .name = "pxa2xx-spi", /* driver to use */ .id = 1, /* Bus number, MUST MATCH SSP number 1..n */ .resource = pxa_ssp1_resources, .num_resources = ARRAY_SIZE(pxa_ssp1_resources), .dev = { .platform_data = &pxa_ssp1_master_info, }, }; #else #define spi1_chips 0 #endif #ifdef CONFIG_SSP2_EN // The set of devices that are on the SPI (2) bus. static struct spi_board_info spi2_chips[] __initdata = { // Enable an spidev "chip" for debugging { .modalias = "spidev", .max_speed_hz = 20 MHZ, // max - PXA only does 13MHz .bus_num = 2, // SSP1 .chip_select = 0, // device number on bus (0-based) }, }; static struct resource pxa_ssp2_resources[] = { { .start = __PREG(SSCR0_P(2)), /* Start address of SSP2 registers */ .end = __PREG(SSCR0_P(2)) + SSP_REG_LEN - 1, .flags = IORESOURCE_MEM, }, { .start = IRQ_SSP2, .end = IRQ_SSP2, .flags = IORESOURCE_IRQ, }, }; static struct pxa2xx_spi_master pxa_ssp2_master_info = { .ssp_type = PXA27x_SSP, .clock_enable = CKEN_SSP2, .num_chipselect = ARRAY_SIZE(spi2_chips), /* number of chips attached to SSP */ .enable_dma = 0, }; static struct platform_device pxa_ssp2 = { .name = "pxa2xx-spi", /* driver to use */ .id = 2, /* Bus number, MUST MATCH SSP number 1..n */ .resource = pxa_ssp2_resources, .num_resources = ARRAY_SIZE(pxa_ssp2_resources), .dev = { .platform_data = &pxa_ssp2_master_info, /* Passed to driver */ }, }; #else #define spi2_chips 0 #endif static struct platform_device *ssp_devices[] __initdata = { #ifdef CONFIG_SSP1_EN &pxa_ssp1, #endif #ifdef CONFIG_SSP2_EN &pxa_ssp2, #endif }; // the set of gpregs that need to be initialized static gpreg* sspregs[] __initdata = { #ifdef CONFIG_SSP1_EN &ssp1_cs0_gpio, &ssp1_cs1_gpio, #endif #ifdef CONFIG_SSP2_EN #endif }; // Module initialization // called from platform extern int __init ft4_ssp_init(void) { pr_info("Installing SSP drivers/devices\n"); /* SSP GPIO setup */ pxa_gpio_init(ssp_gpio_init_list, ARRAY_SIZE(ssp_gpio_init_list)); // initialize our GPIO pins gpreg **reg = sspregs; for (int i = 0; i < ARRAY_SIZE(sspregs); i++, reg++) { if (gpreg_init(*reg)) { return -ENODEV; } } // register ssp controllers int err = platform_add_devices(ssp_devices, ARRAY_SIZE(ssp_devices)); // register spi chips if (spi1_chips) spi_register_board_info(spi1_chips, ARRAY_SIZE(spi1_chips)); if (spi2_chips) spi_register_board_info(spi2_chips, ARRAY_SIZE(spi2_chips)); return err; }