|  | /* | 
|  | * device.c  -- common ColdFire SoC device support | 
|  | * | 
|  | * (C) Copyright 2011, Greg Ungerer <gerg@uclinux.org> | 
|  | * | 
|  | * This file is subject to the terms and conditions of the GNU General Public | 
|  | * License.  See the file COPYING in the main directory of this archive | 
|  | * for more details. | 
|  | */ | 
|  |  | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/spi/spi.h> | 
|  | #include <linux/gpio.h> | 
|  | #include <linux/fec.h> | 
|  | #include <asm/traps.h> | 
|  | #include <asm/coldfire.h> | 
|  | #include <asm/mcfsim.h> | 
|  | #include <asm/mcfuart.h> | 
|  | #include <asm/mcfqspi.h> | 
|  |  | 
|  | /* | 
|  | *	All current ColdFire parts contain from 2, 3, 4 or 10 UARTS. | 
|  | */ | 
|  | static struct mcf_platform_uart mcf_uart_platform_data[] = { | 
|  | { | 
|  | .mapbase	= MCFUART_BASE0, | 
|  | .irq		= MCF_IRQ_UART0, | 
|  | }, | 
|  | { | 
|  | .mapbase	= MCFUART_BASE1, | 
|  | .irq		= MCF_IRQ_UART1, | 
|  | }, | 
|  | #ifdef MCFUART_BASE2 | 
|  | { | 
|  | .mapbase	= MCFUART_BASE2, | 
|  | .irq		= MCF_IRQ_UART2, | 
|  | }, | 
|  | #endif | 
|  | #ifdef MCFUART_BASE3 | 
|  | { | 
|  | .mapbase	= MCFUART_BASE3, | 
|  | .irq		= MCF_IRQ_UART3, | 
|  | }, | 
|  | #endif | 
|  | #ifdef MCFUART_BASE4 | 
|  | { | 
|  | .mapbase	= MCFUART_BASE4, | 
|  | .irq		= MCF_IRQ_UART4, | 
|  | }, | 
|  | #endif | 
|  | #ifdef MCFUART_BASE5 | 
|  | { | 
|  | .mapbase	= MCFUART_BASE5, | 
|  | .irq		= MCF_IRQ_UART5, | 
|  | }, | 
|  | #endif | 
|  | #ifdef MCFUART_BASE6 | 
|  | { | 
|  | .mapbase	= MCFUART_BASE6, | 
|  | .irq		= MCF_IRQ_UART6, | 
|  | }, | 
|  | #endif | 
|  | #ifdef MCFUART_BASE7 | 
|  | { | 
|  | .mapbase	= MCFUART_BASE7, | 
|  | .irq		= MCF_IRQ_UART7, | 
|  | }, | 
|  | #endif | 
|  | #ifdef MCFUART_BASE8 | 
|  | { | 
|  | .mapbase	= MCFUART_BASE8, | 
|  | .irq		= MCF_IRQ_UART8, | 
|  | }, | 
|  | #endif | 
|  | #ifdef MCFUART_BASE9 | 
|  | { | 
|  | .mapbase	= MCFUART_BASE9, | 
|  | .irq		= MCF_IRQ_UART9, | 
|  | }, | 
|  | #endif | 
|  | { }, | 
|  | }; | 
|  |  | 
|  | static struct platform_device mcf_uart = { | 
|  | .name			= "mcfuart", | 
|  | .id			= 0, | 
|  | .dev.platform_data	= mcf_uart_platform_data, | 
|  | }; | 
|  |  | 
|  | #if IS_ENABLED(CONFIG_FEC) | 
|  |  | 
|  | #ifdef CONFIG_M5441x | 
|  | #define FEC_NAME	"enet-fec" | 
|  | static struct fec_platform_data fec_pdata = { | 
|  | .phy		= PHY_INTERFACE_MODE_RMII, | 
|  | }; | 
|  | #define FEC_PDATA	(&fec_pdata) | 
|  | #else | 
|  | #define FEC_NAME	"fec" | 
|  | #define FEC_PDATA	NULL | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | *	Some ColdFire cores contain the Fast Ethernet Controller (FEC) | 
|  | *	block. It is Freescale's own hardware block. Some ColdFires | 
|  | *	have 2 of these. | 
|  | */ | 
|  | static struct resource mcf_fec0_resources[] = { | 
|  | { | 
|  | .start		= MCFFEC_BASE0, | 
|  | .end		= MCFFEC_BASE0 + MCFFEC_SIZE0 - 1, | 
|  | .flags		= IORESOURCE_MEM, | 
|  | }, | 
|  | { | 
|  | .start		= MCF_IRQ_FECRX0, | 
|  | .end		= MCF_IRQ_FECRX0, | 
|  | .flags		= IORESOURCE_IRQ, | 
|  | }, | 
|  | { | 
|  | .start		= MCF_IRQ_FECTX0, | 
|  | .end		= MCF_IRQ_FECTX0, | 
|  | .flags		= IORESOURCE_IRQ, | 
|  | }, | 
|  | { | 
|  | .start		= MCF_IRQ_FECENTC0, | 
|  | .end		= MCF_IRQ_FECENTC0, | 
|  | .flags		= IORESOURCE_IRQ, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static struct platform_device mcf_fec0 = { | 
|  | .name			= FEC_NAME, | 
|  | .id			= 0, | 
|  | .num_resources		= ARRAY_SIZE(mcf_fec0_resources), | 
|  | .resource		= mcf_fec0_resources, | 
|  | .dev = { | 
|  | .dma_mask		= &mcf_fec0.dev.coherent_dma_mask, | 
|  | .coherent_dma_mask	= DMA_BIT_MASK(32), | 
|  | .platform_data		= FEC_PDATA, | 
|  | } | 
|  | }; | 
|  |  | 
|  | #ifdef MCFFEC_BASE1 | 
|  | static struct resource mcf_fec1_resources[] = { | 
|  | { | 
|  | .start		= MCFFEC_BASE1, | 
|  | .end		= MCFFEC_BASE1 + MCFFEC_SIZE1 - 1, | 
|  | .flags		= IORESOURCE_MEM, | 
|  | }, | 
|  | { | 
|  | .start		= MCF_IRQ_FECRX1, | 
|  | .end		= MCF_IRQ_FECRX1, | 
|  | .flags		= IORESOURCE_IRQ, | 
|  | }, | 
|  | { | 
|  | .start		= MCF_IRQ_FECTX1, | 
|  | .end		= MCF_IRQ_FECTX1, | 
|  | .flags		= IORESOURCE_IRQ, | 
|  | }, | 
|  | { | 
|  | .start		= MCF_IRQ_FECENTC1, | 
|  | .end		= MCF_IRQ_FECENTC1, | 
|  | .flags		= IORESOURCE_IRQ, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static struct platform_device mcf_fec1 = { | 
|  | .name			= FEC_NAME, | 
|  | .id			= 1, | 
|  | .num_resources		= ARRAY_SIZE(mcf_fec1_resources), | 
|  | .resource		= mcf_fec1_resources, | 
|  | .dev = { | 
|  | .dma_mask		= &mcf_fec1.dev.coherent_dma_mask, | 
|  | .coherent_dma_mask	= DMA_BIT_MASK(32), | 
|  | .platform_data		= FEC_PDATA, | 
|  | } | 
|  | }; | 
|  | #endif /* MCFFEC_BASE1 */ | 
|  | #endif /* CONFIG_FEC */ | 
|  |  | 
|  | #if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) | 
|  | /* | 
|  | *	The ColdFire QSPI module is an SPI protocol hardware block used | 
|  | *	on a number of different ColdFire CPUs. | 
|  | */ | 
|  | static struct resource mcf_qspi_resources[] = { | 
|  | { | 
|  | .start		= MCFQSPI_BASE, | 
|  | .end		= MCFQSPI_BASE + MCFQSPI_SIZE - 1, | 
|  | .flags		= IORESOURCE_MEM, | 
|  | }, | 
|  | { | 
|  | .start		= MCF_IRQ_QSPI, | 
|  | .end		= MCF_IRQ_QSPI, | 
|  | .flags		= IORESOURCE_IRQ, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static int mcf_cs_setup(struct mcfqspi_cs_control *cs_control) | 
|  | { | 
|  | int status; | 
|  |  | 
|  | status = gpio_request(MCFQSPI_CS0, "MCFQSPI_CS0"); | 
|  | if (status) { | 
|  | pr_debug("gpio_request for MCFQSPI_CS0 failed\n"); | 
|  | goto fail0; | 
|  | } | 
|  | status = gpio_direction_output(MCFQSPI_CS0, 1); | 
|  | if (status) { | 
|  | pr_debug("gpio_direction_output for MCFQSPI_CS0 failed\n"); | 
|  | goto fail1; | 
|  | } | 
|  |  | 
|  | status = gpio_request(MCFQSPI_CS1, "MCFQSPI_CS1"); | 
|  | if (status) { | 
|  | pr_debug("gpio_request for MCFQSPI_CS1 failed\n"); | 
|  | goto fail1; | 
|  | } | 
|  | status = gpio_direction_output(MCFQSPI_CS1, 1); | 
|  | if (status) { | 
|  | pr_debug("gpio_direction_output for MCFQSPI_CS1 failed\n"); | 
|  | goto fail2; | 
|  | } | 
|  |  | 
|  | status = gpio_request(MCFQSPI_CS2, "MCFQSPI_CS2"); | 
|  | if (status) { | 
|  | pr_debug("gpio_request for MCFQSPI_CS2 failed\n"); | 
|  | goto fail2; | 
|  | } | 
|  | status = gpio_direction_output(MCFQSPI_CS2, 1); | 
|  | if (status) { | 
|  | pr_debug("gpio_direction_output for MCFQSPI_CS2 failed\n"); | 
|  | goto fail3; | 
|  | } | 
|  |  | 
|  | #ifdef MCFQSPI_CS3 | 
|  | status = gpio_request(MCFQSPI_CS3, "MCFQSPI_CS3"); | 
|  | if (status) { | 
|  | pr_debug("gpio_request for MCFQSPI_CS3 failed\n"); | 
|  | goto fail3; | 
|  | } | 
|  | status = gpio_direction_output(MCFQSPI_CS3, 1); | 
|  | if (status) { | 
|  | pr_debug("gpio_direction_output for MCFQSPI_CS3 failed\n"); | 
|  | gpio_free(MCFQSPI_CS3); | 
|  | goto fail3; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | fail3: | 
|  | gpio_free(MCFQSPI_CS2); | 
|  | fail2: | 
|  | gpio_free(MCFQSPI_CS1); | 
|  | fail1: | 
|  | gpio_free(MCFQSPI_CS0); | 
|  | fail0: | 
|  | return status; | 
|  | } | 
|  |  | 
|  | static void mcf_cs_teardown(struct mcfqspi_cs_control *cs_control) | 
|  | { | 
|  | #ifdef MCFQSPI_CS3 | 
|  | gpio_free(MCFQSPI_CS3); | 
|  | #endif | 
|  | gpio_free(MCFQSPI_CS2); | 
|  | gpio_free(MCFQSPI_CS1); | 
|  | gpio_free(MCFQSPI_CS0); | 
|  | } | 
|  |  | 
|  | static void mcf_cs_select(struct mcfqspi_cs_control *cs_control, | 
|  | u8 chip_select, bool cs_high) | 
|  | { | 
|  | switch (chip_select) { | 
|  | case 0: | 
|  | gpio_set_value(MCFQSPI_CS0, cs_high); | 
|  | break; | 
|  | case 1: | 
|  | gpio_set_value(MCFQSPI_CS1, cs_high); | 
|  | break; | 
|  | case 2: | 
|  | gpio_set_value(MCFQSPI_CS2, cs_high); | 
|  | break; | 
|  | #ifdef MCFQSPI_CS3 | 
|  | case 3: | 
|  | gpio_set_value(MCFQSPI_CS3, cs_high); | 
|  | break; | 
|  | #endif | 
|  | } | 
|  | } | 
|  |  | 
|  | static void mcf_cs_deselect(struct mcfqspi_cs_control *cs_control, | 
|  | u8 chip_select, bool cs_high) | 
|  | { | 
|  | switch (chip_select) { | 
|  | case 0: | 
|  | gpio_set_value(MCFQSPI_CS0, !cs_high); | 
|  | break; | 
|  | case 1: | 
|  | gpio_set_value(MCFQSPI_CS1, !cs_high); | 
|  | break; | 
|  | case 2: | 
|  | gpio_set_value(MCFQSPI_CS2, !cs_high); | 
|  | break; | 
|  | #ifdef MCFQSPI_CS3 | 
|  | case 3: | 
|  | gpio_set_value(MCFQSPI_CS3, !cs_high); | 
|  | break; | 
|  | #endif | 
|  | } | 
|  | } | 
|  |  | 
|  | static struct mcfqspi_cs_control mcf_cs_control = { | 
|  | .setup			= mcf_cs_setup, | 
|  | .teardown		= mcf_cs_teardown, | 
|  | .select			= mcf_cs_select, | 
|  | .deselect		= mcf_cs_deselect, | 
|  | }; | 
|  |  | 
|  | static struct mcfqspi_platform_data mcf_qspi_data = { | 
|  | .bus_num		= 0, | 
|  | .num_chipselect		= 4, | 
|  | .cs_control		= &mcf_cs_control, | 
|  | }; | 
|  |  | 
|  | static struct platform_device mcf_qspi = { | 
|  | .name			= "mcfqspi", | 
|  | .id			= 0, | 
|  | .num_resources		= ARRAY_SIZE(mcf_qspi_resources), | 
|  | .resource		= mcf_qspi_resources, | 
|  | .dev.platform_data	= &mcf_qspi_data, | 
|  | }; | 
|  | #endif /* IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) */ | 
|  |  | 
|  | #if IS_ENABLED(CONFIG_I2C_IMX) | 
|  | static struct resource mcf_i2c0_resources[] = { | 
|  | { | 
|  | .start          = MCFI2C_BASE0, | 
|  | .end            = MCFI2C_BASE0 + MCFI2C_SIZE0 - 1, | 
|  | .flags          = IORESOURCE_MEM, | 
|  | }, | 
|  | { | 
|  | .start          = MCF_IRQ_I2C0, | 
|  | .end            = MCF_IRQ_I2C0, | 
|  | .flags          = IORESOURCE_IRQ, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static struct platform_device mcf_i2c0 = { | 
|  | .name                   = "imx1-i2c", | 
|  | .id                     = 0, | 
|  | .num_resources          = ARRAY_SIZE(mcf_i2c0_resources), | 
|  | .resource               = mcf_i2c0_resources, | 
|  | }; | 
|  | #ifdef MCFI2C_BASE1 | 
|  |  | 
|  | static struct resource mcf_i2c1_resources[] = { | 
|  | { | 
|  | .start          = MCFI2C_BASE1, | 
|  | .end            = MCFI2C_BASE1 + MCFI2C_SIZE1 - 1, | 
|  | .flags          = IORESOURCE_MEM, | 
|  | }, | 
|  | { | 
|  | .start          = MCF_IRQ_I2C1, | 
|  | .end            = MCF_IRQ_I2C1, | 
|  | .flags          = IORESOURCE_IRQ, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static struct platform_device mcf_i2c1 = { | 
|  | .name                   = "imx1-i2c", | 
|  | .id                     = 1, | 
|  | .num_resources          = ARRAY_SIZE(mcf_i2c1_resources), | 
|  | .resource               = mcf_i2c1_resources, | 
|  | }; | 
|  |  | 
|  | #endif /* MCFI2C_BASE1 */ | 
|  |  | 
|  | #ifdef MCFI2C_BASE2 | 
|  |  | 
|  | static struct resource mcf_i2c2_resources[] = { | 
|  | { | 
|  | .start          = MCFI2C_BASE2, | 
|  | .end            = MCFI2C_BASE2 + MCFI2C_SIZE2 - 1, | 
|  | .flags          = IORESOURCE_MEM, | 
|  | }, | 
|  | { | 
|  | .start          = MCF_IRQ_I2C2, | 
|  | .end            = MCF_IRQ_I2C2, | 
|  | .flags          = IORESOURCE_IRQ, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static struct platform_device mcf_i2c2 = { | 
|  | .name                   = "imx1-i2c", | 
|  | .id                     = 2, | 
|  | .num_resources          = ARRAY_SIZE(mcf_i2c2_resources), | 
|  | .resource               = mcf_i2c2_resources, | 
|  | }; | 
|  |  | 
|  | #endif /* MCFI2C_BASE2 */ | 
|  |  | 
|  | #ifdef MCFI2C_BASE3 | 
|  |  | 
|  | static struct resource mcf_i2c3_resources[] = { | 
|  | { | 
|  | .start          = MCFI2C_BASE3, | 
|  | .end            = MCFI2C_BASE3 + MCFI2C_SIZE3 - 1, | 
|  | .flags          = IORESOURCE_MEM, | 
|  | }, | 
|  | { | 
|  | .start          = MCF_IRQ_I2C3, | 
|  | .end            = MCF_IRQ_I2C3, | 
|  | .flags          = IORESOURCE_IRQ, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static struct platform_device mcf_i2c3 = { | 
|  | .name                   = "imx1-i2c", | 
|  | .id                     = 3, | 
|  | .num_resources          = ARRAY_SIZE(mcf_i2c3_resources), | 
|  | .resource               = mcf_i2c3_resources, | 
|  | }; | 
|  |  | 
|  | #endif /* MCFI2C_BASE3 */ | 
|  |  | 
|  | #ifdef MCFI2C_BASE4 | 
|  |  | 
|  | static struct resource mcf_i2c4_resources[] = { | 
|  | { | 
|  | .start          = MCFI2C_BASE4, | 
|  | .end            = MCFI2C_BASE4 + MCFI2C_SIZE4 - 1, | 
|  | .flags          = IORESOURCE_MEM, | 
|  | }, | 
|  | { | 
|  | .start          = MCF_IRQ_I2C4, | 
|  | .end            = MCF_IRQ_I2C4, | 
|  | .flags          = IORESOURCE_IRQ, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static struct platform_device mcf_i2c4 = { | 
|  | .name                   = "imx1-i2c", | 
|  | .id                     = 4, | 
|  | .num_resources          = ARRAY_SIZE(mcf_i2c4_resources), | 
|  | .resource               = mcf_i2c4_resources, | 
|  | }; | 
|  |  | 
|  | #endif /* MCFI2C_BASE4 */ | 
|  |  | 
|  | #ifdef MCFI2C_BASE5 | 
|  |  | 
|  | static struct resource mcf_i2c5_resources[] = { | 
|  | { | 
|  | .start          = MCFI2C_BASE5, | 
|  | .end            = MCFI2C_BASE5 + MCFI2C_SIZE5 - 1, | 
|  | .flags          = IORESOURCE_MEM, | 
|  | }, | 
|  | { | 
|  | .start          = MCF_IRQ_I2C5, | 
|  | .end            = MCF_IRQ_I2C5, | 
|  | .flags          = IORESOURCE_IRQ, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static struct platform_device mcf_i2c5 = { | 
|  | .name                   = "imx1-i2c", | 
|  | .id                     = 5, | 
|  | .num_resources          = ARRAY_SIZE(mcf_i2c5_resources), | 
|  | .resource               = mcf_i2c5_resources, | 
|  | }; | 
|  |  | 
|  | #endif /* MCFI2C_BASE5 */ | 
|  | #endif /* IS_ENABLED(CONFIG_I2C_IMX) */ | 
|  |  | 
|  | static struct platform_device *mcf_devices[] __initdata = { | 
|  | &mcf_uart, | 
|  | #if IS_ENABLED(CONFIG_FEC) | 
|  | &mcf_fec0, | 
|  | #ifdef MCFFEC_BASE1 | 
|  | &mcf_fec1, | 
|  | #endif | 
|  | #endif | 
|  | #if IS_ENABLED(CONFIG_SPI_COLDFIRE_QSPI) | 
|  | &mcf_qspi, | 
|  | #endif | 
|  | #if IS_ENABLED(CONFIG_I2C_IMX) | 
|  | &mcf_i2c0, | 
|  | #ifdef MCFI2C_BASE1 | 
|  | &mcf_i2c1, | 
|  | #endif | 
|  | #ifdef MCFI2C_BASE2 | 
|  | &mcf_i2c2, | 
|  | #endif | 
|  | #ifdef MCFI2C_BASE3 | 
|  | &mcf_i2c3, | 
|  | #endif | 
|  | #ifdef MCFI2C_BASE4 | 
|  | &mcf_i2c4, | 
|  | #endif | 
|  | #ifdef MCFI2C_BASE5 | 
|  | &mcf_i2c5, | 
|  | #endif | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | /* | 
|  | *	Some ColdFire UARTs let you set the IRQ line to use. | 
|  | */ | 
|  | static void __init mcf_uart_set_irq(void) | 
|  | { | 
|  | #ifdef MCFUART_UIVR | 
|  | /* UART0 interrupt setup */ | 
|  | writeb(MCFSIM_ICR_LEVEL6 | MCFSIM_ICR_PRI1, MCFSIM_UART1ICR); | 
|  | writeb(MCF_IRQ_UART0, MCFUART_BASE0 + MCFUART_UIVR); | 
|  | mcf_mapirq2imr(MCF_IRQ_UART0, MCFINTC_UART0); | 
|  |  | 
|  | /* UART1 interrupt setup */ | 
|  | writeb(MCFSIM_ICR_LEVEL6 | MCFSIM_ICR_PRI2, MCFSIM_UART2ICR); | 
|  | writeb(MCF_IRQ_UART1, MCFUART_BASE1 + MCFUART_UIVR); | 
|  | mcf_mapirq2imr(MCF_IRQ_UART1, MCFINTC_UART1); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | static int __init mcf_init_devices(void) | 
|  | { | 
|  | mcf_uart_set_irq(); | 
|  | platform_add_devices(mcf_devices, ARRAY_SIZE(mcf_devices)); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | arch_initcall(mcf_init_devices); | 
|  |  |