diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2013-03-04 09:21:54 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2013-03-04 09:21:54 +0100 |
commit | ea0f831cd0c749194a0103ec4814fe91c3955084 (patch) | |
tree | 9a69ef4476e29cf61e53058c1452cc3afd27be6c /drivers | |
parent | 62ee96bd3b5e02173c0d9956c4f1e1a4de8f940d (diff) | |
parent | 439eefcfe40d7ca821985d969312c4e90f7a1a7b (diff) | |
download | barebox-ea0f831cd0c749194a0103ec4814fe91c3955084.tar.gz barebox-ea0f831cd0c749194a0103ec4814fe91c3955084.tar.xz |
Merge branch 'for-next/mxs'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mci/mxs.c | 118 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 6 | ||||
-rw-r--r-- | drivers/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/serial/serial_auart.c | 238 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 5 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/mxs_spi.c | 289 |
7 files changed, 540 insertions, 118 deletions
diff --git a/drivers/mci/mxs.c b/drivers/mci/mxs.c index c65796bb93..3657b3e496 100644 --- a/drivers/mci/mxs.c +++ b/drivers/mci/mxs.c @@ -40,127 +40,11 @@ #include <mach/imx-regs.h> #include <mach/mci.h> #include <mach/clock.h> +#include <mach/ssp.h> #define CLOCKRATE_MIN (1 * 1000 * 1000) #define CLOCKRATE_MAX (480 * 1000 * 1000) -#define HW_SSP_CTRL0 0x000 -# define SSP_CTRL0_SFTRST (1 << 31) -# define SSP_CTRL0_CLKGATE (1 << 30) -# define SSP_CTRL0_RUN (1 << 29) -# define SSP_CTRL0_LOCK_CS (1 << 27) -# define SSP_CTRL0_READ (1 << 25) -# define SSP_CTRL0_IGNORE_CRC (1 << 26) -# define SSP_CTRL0_DATA_XFER (1 << 24) -# define SSP_CTRL0_BUS_WIDTH(x) (((x) & 0x3) << 22) -# define SSP_CTRL0_WAIT_FOR_IRQ (1 << 21) -# define SSP_CTRL0_LONG_RESP (1 << 19) -# define SSP_CTRL0_GET_RESP (1 << 17) -# define SSP_CTRL0_ENABLE (1 << 16) -#ifdef CONFIG_ARCH_IMX23 -# define SSP_CTRL0_XFER_COUNT(x) ((x) & 0xffff) -#endif - -#define HW_SSP_CMD0 0x010 -# define SSP_CMD0_SLOW_CLK (1 << 22) -# define SSP_CMD0_CONT_CLK (1 << 21) -# define SSP_CMD0_APPEND_8CYC (1 << 20) -#ifdef CONFIG_ARCH_IMX23 -# define SSP_CMD0_BLOCK_SIZE(x) (((x) & 0xf) << 16) -# define SSP_CMD0_BLOCK_COUNT(x) (((x) & 0xff) << 8) -#endif -# define SSP_CMD0_CMD(x) ((x) & 0xff) - -#define HW_SSP_CMD1 0x020 - -#ifdef CONFIG_ARCH_IMX23 -# define HW_SSP_COMPREF 0x030 -# define HW_SSP_COMPMASK 0x040 -# define HW_SSP_TIMING 0x050 -# define HW_SSP_CTRL1 0x060 -# define HW_SSP_DATA 0x070 -#endif -#ifdef CONFIG_ARCH_IMX28 -# define HW_SSP_XFER_COUNT 0x30 -# define HW_SSP_BLOCK_SIZE 0x40 -# define SSP_BLOCK_SIZE(x) ((x) & 0xf) -# define SSP_BLOCK_COUNT(x) (((x) & 0xffffff) << 4) -# define HW_SSP_COMPREF 0x050 -# define HW_SSP_COMPMASK 0x060 -# define HW_SSP_TIMING 0x070 -# define HW_SSP_CTRL1 0x080 -# define HW_SSP_DATA 0x090 -#endif -/* bit definition for register HW_SSP_TIMING */ -# define SSP_TIMING_TIMEOUT_MASK (0xffff0000) -# define SSP_TIMING_TIMEOUT(x) ((x) << 16) -# define SSP_TIMING_CLOCK_DIVIDE(x) (((x) & 0xff) << 8) -# define SSP_TIMING_CLOCK_RATE(x) ((x) & 0xff) - -/* bit definition for register HW_SSP_CTRL1 */ -# define SSP_CTRL1_POLARITY (1 << 9) -# define SSP_CTRL1_WORD_LENGTH(x) (((x) & 0xf) << 4) -# define SSP_CTRL1_SSP_MODE(x) ((x) & 0xf) - -#ifdef CONFIG_ARCH_IMX23 -# define HW_SSP_SDRESP0 0x080 -# define HW_SSP_SDRESP1 0x090 -# define HW_SSP_SDRESP2 0x0A0 -# define HW_SSP_SDRESP3 0x0B0 -#endif -#ifdef CONFIG_ARCH_IMX28 -# define HW_SSP_SDRESP0 0x0A0 -# define HW_SSP_SDRESP1 0x0B0 -# define HW_SSP_SDRESP2 0x0C0 -# define HW_SSP_SDRESP3 0x0D0 -#endif - -#ifdef CONFIG_ARCH_IMX28 -# define HW_SSP_DDR_CTRL 0x0E0 -# define HW_SSP_DLL_CTRL 0x0F0 -#endif - -#ifdef CONFIG_ARCH_IMX23 -# define HW_SSP_STATUS 0x0C0 -#endif -#ifdef CONFIG_ARCH_IMX28 -# define HW_SSP_STATUS 0x100 -#endif - -/* bit definition for register HW_SSP_STATUS */ -# define SSP_STATUS_PRESENT (1 << 31) -# define SSP_STATUS_SD_PRESENT (1 << 29) -# define SSP_STATUS_CARD_DETECT (1 << 28) -# define SSP_STATUS_RESP_CRC_ERR (1 << 16) -# define SSP_STATUS_RESP_ERR (1 << 15) -# define SSP_STATUS_RESP_TIMEOUT (1 << 14) -# define SSP_STATUS_DATA_CRC_ERR (1 << 13) -# define SSP_STATUS_TIMEOUT (1 << 12) -# define SSP_STATUS_FIFO_OVRFLW (1 << 9) -# define SSP_STATUS_FIFO_FULL (1 << 8) -# define SSP_STATUS_FIFO_EMPTY (1 << 5) -# define SSP_STATUS_FIFO_UNDRFLW (1 << 4) -# define SSP_STATUS_CMD_BUSY (1 << 3) -# define SSP_STATUS_DATA_BUSY (1 << 2) -# define SSP_STATUS_BUSY (1 << 0) -# define SSP_STATUS_ERROR (SSP_STATUS_FIFO_OVRFLW | SSP_STATUS_FIFO_UNDRFLW | \ - SSP_STATUS_RESP_CRC_ERR | SSP_STATUS_RESP_ERR | \ - SSP_STATUS_RESP_TIMEOUT | SSP_STATUS_DATA_CRC_ERR | SSP_STATUS_TIMEOUT) - -#ifdef CONFIG_ARCH_IMX28 -# define HW_SSP_DLL_STS 0x110 -#endif - -#ifdef CONFIG_ARCH_IMX23 -# define HW_SSP_DEBUG 0x100 -# define HW_SSP_VERSION 0x110 -#endif - -#ifdef CONFIG_ARCH_IMX28 -# define HW_SSP_DEBUG 0x120 -# define HW_SSP_VERSION 0x130 -#endif - struct mxs_mci_host { struct mci_host host; void __iomem *regs; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index f61d6708a4..48bea603e8 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -22,7 +22,11 @@ config DRIVER_SERIAL_IMX config DRIVER_SERIAL_STM378X depends on ARCH_MXS default y - bool "i.MX23/i.MX28 serial driver" + bool "i.MX23/i.MX28 debug UART serial driver" + +config DRIVER_SERIAL_AUART + depends on ARCH_MXS + bool "i.MX23/i.MX28 application UART serial driver" config DRIVER_SERIAL_NETX depends on ARCH_NETX diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 16a54c268a..4a23aefddb 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_DRIVER_SERIAL_ALTERA) += serial_altera.o obj-$(CONFIG_DRIVER_SERIAL_ALTERA_JTAG) += serial_altera_jtag.o obj-$(CONFIG_DRIVER_SERIAL_PXA) += serial_pxa.o obj-$(CONFIG_DRIVER_SERIAL_OMAP4_USBBOOT) += serial_omap4_usbboot.o +obj-$(CONFIG_DRIVER_SERIAL_AUART) += serial_auart.o diff --git a/drivers/serial/serial_auart.c b/drivers/serial/serial_auart.c new file mode 100644 index 0000000000..fa2e04f447 --- /dev/null +++ b/drivers/serial/serial_auart.c @@ -0,0 +1,238 @@ +/* + * (C) 2013 Marc Kleine-Budde <mkl@pengutronix.de> + * + * Based on the stm-serial driver: + * + * (C) Copyright 2010 Juergen Beisert - Pengutronix + * + * ...also based on the u-boot auart driver: + * + * (C) 2011 Wolfgang Ocker <weo@reccoware.de> + * + * Based on the standard DUART serial driver: + * + * (C) 2007 Sascha Hauer <s.hauer@pengutronix.de> + * + * (C) Copyright 2009-2010 Freescale Semiconductor, Inc. + * + * Further based on the Linux mxs-auart.c driver: + * + * Freescale STMP37XX/STMP378X Application UART driver + * + * Author: dmitry pervushin <dimka@embeddedalley.com> + * + * Copyright 2008-2010 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, 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 as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <common.h> +#include <gpio.h> +#include <init.h> +#include <io.h> +#include <malloc.h> +#include <notifier.h> + +#include <mach/clock.h> +#include <mach/mxs.h> +#include <mach/imx-regs.h> + +#define HW_UARTAPP_CTRL0 (0x00000000) + +#define HW_UARTAPP_CTRL2 (0x00000020) +#define HW_UARTAPP_CTRL2_SET (0x00000024) +#define HW_UARTAPP_CTRL2_CLR (0x00000028) +#define BM_UARTAPP_CTRL2_CTSEN (0x00008000) +#define BM_UARTAPP_CTRL2_RTSEN (0x00004000) +#define BM_UARTAPP_CTRL2_RXE (0x00000200) +#define BM_UARTAPP_CTRL2_TXE (0x00000100) +#define BM_UARTAPP_CTRL2_USE_LCR2 (0x00000040) +#define BM_UARTAPP_CTRL2_UARTEN (0x00000001) + +#define HW_UARTAPP_LINECTRL (0x00000030) +#define BM_UARTAPP_LINECTRL_FEN (0x00000010) + +#define BM_UARTAPP_LINECTRL_BAUD_DIVFRAC (0x00003F00) +#define BF_UARTAPP_LINECTRL_BAUD_DIVFRAC(v) \ + (((v) << 8) & BM_UARTAPP_LINECTRL_BAUD_DIVFRAC) + +#define BP_UARTAPP_LINECTRL_BAUD_DIVINT (16) +#define BM_UARTAPP_LINECTRL_BAUD_DIVINT (0xFFFF0000) +#define BF_UARTAPP_LINECTRL_BAUD_DIVINT(v) \ + (((v) << 16) & BM_UARTAPP_LINECTRL_BAUD_DIVINT) + +#define BP_UARTAPP_LINECTRL_WLEN (5) +#define BM_UARTAPP_LINECTRL_WLEN (0x00000060) +#define BF_UARTAPP_LINECTRL_WLEN(v) \ + (((v) << 5) & BM_UARTAPP_LINECTRL_WLEN) + +#define HW_UARTAPP_LINECTRL2_SET (0x00000044) + +#define HW_UARTAPP_INTR (0x00000050) + +#define HW_UARTAPP_DATA (0x00000060) +#define BM_UARTAPP_STAT_RXFE (0x01000000) +#define BM_UARTAPP_STAT_TXFE (0x08000000) + +#define HW_UARTAPP_STAT (0x00000070) +#define BM_UARTAPP_STAT_TXFF (0x02000000) + +struct auart_priv { + struct console_device cdev; + int baudrate; + struct notifier_block notify; + void __iomem *base; +}; + +static void auart_serial_putc(struct console_device *cdev, char c) +{ + struct auart_priv *priv = container_of(cdev, struct auart_priv, cdev); + + /* Wait for room in TX FIFO */ + while (readl(priv->base + HW_UARTAPP_STAT) & BM_UARTAPP_STAT_TXFF) + ; + + writel(c, priv->base + HW_UARTAPP_DATA); +} + +static int auart_serial_tstc(struct console_device *cdev) +{ + struct auart_priv *priv = container_of(cdev, struct auart_priv, cdev); + + /* Check if RX FIFO is not empty */ + return !(readl(priv->base + HW_UARTAPP_STAT) & BM_UARTAPP_STAT_RXFE); +} + +static int auart_serial_getc(struct console_device *cdev) +{ + struct auart_priv *priv = container_of(cdev, struct auart_priv, cdev); + + /* Wait while RX FIFO is empty */ + while (!auart_serial_tstc(cdev)) + ; + + return readl(priv->base + HW_UARTAPP_DATA) & 0xff; +} + +static void auart_serial_flush(struct console_device *cdev) +{ + struct auart_priv *priv = container_of(cdev, struct auart_priv, cdev); + + /* Wait for TX FIFO empty */ + while (readl(priv->base + HW_UARTAPP_STAT) & BM_UARTAPP_STAT_TXFE) + ; +} + +static int auart_serial_setbaudrate(struct console_device *cdev, int new_baudrate) +{ + struct auart_priv *priv = container_of(cdev, struct auart_priv, cdev); + uint32_t ctrl2, quot, reg; + + /* Disable everything */ + ctrl2 = readl(priv->base + HW_UARTAPP_CTRL2); + writel(0x0, priv->base + HW_UARTAPP_CTRL2); + + /* Calculate and set baudrate */ + quot = (imx_get_xclk() * 32) / new_baudrate; + reg = BF_UARTAPP_LINECTRL_BAUD_DIVFRAC(quot & 0x3F) | + BF_UARTAPP_LINECTRL_BAUD_DIVINT(quot >> 6) | + BF_UARTAPP_LINECTRL_WLEN(3) | + BM_UARTAPP_LINECTRL_FEN; + + writel(reg, priv->base + HW_UARTAPP_LINECTRL); + + /* Re-enable UART */ + writel(ctrl2, priv->base + HW_UARTAPP_CTRL2); + + priv->baudrate = new_baudrate; + + return 0; +} + +static int auart_clocksource_clock_change(struct notifier_block *nb, unsigned long event, void *data) +{ + struct auart_priv *priv = container_of(nb, struct auart_priv, notify); + + return auart_serial_setbaudrate(&priv->cdev, priv->baudrate); +} + +static void auart_serial_init_port(struct auart_priv *priv) +{ + mxs_reset_block(priv->base + HW_UARTAPP_CTRL0, 0); + + /* Disable UART */ + writel(0x0, priv->base + HW_UARTAPP_CTRL2); + /* Mask interrupts */ + writel(0x0, priv->base + HW_UARTAPP_INTR); +} + +static int auart_serial_probe(struct device_d *dev) +{ + struct auart_priv *priv; + struct console_device *cdev; + + priv = xzalloc(sizeof *priv); + cdev = &priv->cdev; + + cdev->f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR; + cdev->tstc = auart_serial_tstc; + cdev->putc = auart_serial_putc; + cdev->getc = auart_serial_getc; + cdev->flush = auart_serial_flush; + cdev->setbrg = auart_serial_setbaudrate; + cdev->dev = dev; + + dev->priv = priv; + priv->base = dev_request_mem_region(dev, 0); + + auart_serial_init_port(priv); + auart_serial_setbaudrate(cdev, CONFIG_BAUDRATE); + + /* Disable RTS/CTS, enable Rx, Tx, UART */ + writel(BM_UARTAPP_CTRL2_RTSEN | BM_UARTAPP_CTRL2_CTSEN | + BM_UARTAPP_CTRL2_USE_LCR2, + priv->base + HW_UARTAPP_CTRL2_CLR); + writel(BM_UARTAPP_CTRL2_RXE | BM_UARTAPP_CTRL2_TXE | + BM_UARTAPP_CTRL2_UARTEN, + priv->base + HW_UARTAPP_CTRL2_SET); + + console_register(cdev); + priv->notify.notifier_call = auart_clocksource_clock_change; + clock_register_client(&priv->notify); + + return 0; +} + + +static void auart_serial_remove(struct device_d *dev) +{ + struct auart_priv *priv = dev->priv; + + auart_serial_flush(&priv->cdev); + console_unregister(&priv->cdev); + free(priv); +} + +static struct driver_d auart_serial_driver = { + .name = "auart_serial", + .probe = auart_serial_probe, + .remove = auart_serial_remove, +}; + +static int auart_serial_init(void) +{ + platform_driver_register(&auart_serial_driver); + return 0; +} + +console_initcall(auart_serial_init); diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 10b8fea7bd..f14e28f545 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -33,6 +33,11 @@ config DRIVER_SPI_IMX_2_3 depends on ARCH_IMX51 || ARCH_IMX53 || ARCH_IMX6 default y +config DRIVER_SPI_MXS + bool "i.MX (23,28) SPI Master driver" + depends on ARCH_IMX23 || ARCH_IMX28 + depends on SPI + config DRIVER_SPI_OMAP3 bool "OMAP3 McSPI Master driver" depends on ARCH_OMAP3 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index b53061edfc..642b7ecc4a 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_SPI) += spi.o obj-$(CONFIG_DRIVER_SPI_IMX) += imx_spi.o +obj-$(CONFIG_DRIVER_SPI_MXS) += mxs_spi.o obj-$(CONFIG_DRIVER_SPI_ALTERA) += altera_spi.o obj-$(CONFIG_DRIVER_SPI_ATMEL) += atmel_spi.o obj-$(CONFIG_DRIVER_SPI_OMAP3) += omap3_spi.o diff --git a/drivers/spi/mxs_spi.c b/drivers/spi/mxs_spi.c new file mode 100644 index 0000000000..89b4d19b59 --- /dev/null +++ b/drivers/spi/mxs_spi.c @@ -0,0 +1,289 @@ +/* + * Freescale i.MX28 SPI driver + * + * Copyright (C) 2013 Michael Grzeschik <mgr@pengutronix.de> + * + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * on behalf of DENX Software Engineering GmbH + * + * 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 <common.h> +#include <init.h> +#include <malloc.h> +#include <spi/spi.h> +#include <clock.h> +#include <errno.h> +#include <io.h> +#include <linux/clk.h> +#include <asm/mmu.h> +#include <mach/generic.h> +#include <mach/imx-regs.h> +#include <mach/mxs.h> +#include <mach/clock.h> +#include <mach/ssp.h> + +#define MXS_SPI_MAX_TIMEOUT (10 * MSECOND) + +#define SPI_XFER_BEGIN 0x01 /* Assert CS before transfer */ +#define SPI_XFER_END 0x02 /* Deassert CS after transfer */ + +struct mxs_spi { + struct spi_master master; + uint32_t max_khz; + uint32_t mode; + struct clk *clk; + void __iomem *regs; +}; + +static inline struct mxs_spi *to_mxs(struct spi_master *master) +{ + return container_of(master, struct mxs_spi, master); +} + +/* + * Set SSP/MMC bus frequency, in kHz + */ +static void imx_set_ssp_busclock(struct spi_master *master, uint32_t freq) +{ + struct mxs_spi *mxs = to_mxs(master); + const uint32_t sspclk = imx_get_sspclk(master->bus_num); + uint32_t val; + uint32_t divide, rate, tgtclk; + + /* + * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)), + * CLOCK_DIVIDE has to be an even value from 2 to 254, and + * CLOCK_RATE could be any integer from 0 to 255. + */ + for (divide = 2; divide < 254; divide += 2) { + rate = sspclk / freq / divide; + if (rate <= 256) + break; + } + + tgtclk = sspclk / divide / rate; + while (tgtclk > freq) { + rate++; + tgtclk = sspclk / divide / rate; + } + if (rate > 256) + rate = 256; + + /* Always set timeout the maximum */ + val = SSP_TIMING_TIMEOUT_MASK | + SSP_TIMING_CLOCK_DIVIDE(divide) | + SSP_TIMING_CLOCK_RATE(rate - 1); + writel(val, mxs->regs + HW_SSP_TIMING); + + dev_dbg(master->dev, "SPI%d: Set freq rate to %d KHz (requested %d KHz)\n", + master->bus_num, tgtclk, freq); +} + +static int mxs_spi_setup(struct spi_device *spi) +{ + struct spi_master *master = spi->master; + struct mxs_spi *mxs = to_mxs(master); + uint32_t val = 0; + + /* MXS SPI: 4 ports and 3 chip selects maximum */ + if (master->bus_num > 3 || spi->chip_select > 2) { + dev_err(master->dev, "mxs_spi: invalid bus %d / chip select %d\n", + master->bus_num, spi->chip_select); + return -EINVAL; + } + + mxs_reset_block(mxs->regs + HW_SSP_CTRL0, 0); + + val |= SSP_CTRL0_SSP_ASSERT_OUT(spi->chip_select); + val |= SSP_CTRL0_BUS_WIDTH(0); + writel(val, mxs->regs + HW_SSP_CTRL0 + BIT_SET); + + val = SSP_CTRL1_SSP_MODE(0) | SSP_CTRL1_WORD_LENGTH(7); + val |= (mxs->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0; + val |= (mxs->mode & SPI_CPHA) ? SSP_CTRL1_PHASE : 0; + writel(val, mxs->regs + HW_SSP_CTRL1); + + writel(0x0, mxs->regs + HW_SSP_CMD0); + writel(0x0, mxs->regs + HW_SSP_CMD1); + + imx_set_ssp_busclock(master, spi->max_speed_hz); + + return 0; +} + +static void mxs_spi_start_xfer(struct mxs_spi *mxs) +{ + writel(SSP_CTRL0_LOCK_CS, mxs->regs + HW_SSP_CTRL0 + BIT_SET); + writel(SSP_CTRL0_IGNORE_CRC, mxs->regs + HW_SSP_CTRL0 + BIT_CLR); +} + +static void mxs_spi_end_xfer(struct mxs_spi *mxs) +{ + writel(SSP_CTRL0_LOCK_CS, mxs->regs + HW_SSP_CTRL0 + BIT_CLR); + writel(SSP_CTRL0_IGNORE_CRC, mxs->regs + HW_SSP_CTRL0 + BIT_SET); +} + +static void mxs_spi_set_cs(struct spi_device *spi) +{ + struct mxs_spi *mxs = to_mxs(spi->master); + const uint32_t mask = SSP_CTRL0_WAIT_FOR_CMD | SSP_CTRL0_WAIT_FOR_IRQ; + uint32_t select = SSP_CTRL0_SSP_ASSERT_OUT(spi->chip_select); + + writel(mask, mxs->regs + HW_SSP_CTRL0 + BIT_CLR); + writel(select, mxs->regs + HW_SSP_CTRL0 + BIT_SET); +} + +static int mxs_spi_xfer_pio(struct spi_device *spi, + char *data, int length, int write, unsigned long flags) +{ + struct mxs_spi *mxs = to_mxs(spi->master); + struct spi_master *master = spi->master; + + if (flags & SPI_XFER_BEGIN) + mxs_spi_start_xfer(mxs); + + mxs_spi_set_cs(spi); + + while (length--) { + if ((flags & SPI_XFER_END) && !length) + mxs_spi_end_xfer(mxs); + + /* We transfer 1 byte */ + writel(1, mxs->regs + HW_SSP_XFER_COUNT); + + if (write) + writel(SSP_CTRL0_READ, mxs->regs + HW_SSP_CTRL0 + BIT_CLR); + else + writel(SSP_CTRL0_READ, mxs->regs + HW_SSP_CTRL0 + BIT_SET); + + writel(SSP_CTRL0_RUN, mxs->regs + HW_SSP_CTRL0 + BIT_SET); + + if (wait_on_timeout(MXS_SPI_MAX_TIMEOUT, + (readl(mxs->regs + HW_SSP_CTRL0) & SSP_CTRL0_RUN) == SSP_CTRL0_RUN)) { + dev_err(master->dev, "MXS SPI: Timeout waiting for start\n"); + return -ETIMEDOUT; + } + + if (write) + writel(*data++, mxs->regs + HW_SSP_DATA); + + writel(SSP_CTRL0_DATA_XFER, mxs->regs + HW_SSP_CTRL0 + BIT_SET); + + if (!write) { + if (wait_on_timeout(MXS_SPI_MAX_TIMEOUT, + !(readl(mxs->regs + HW_SSP_STATUS) & SSP_STATUS_FIFO_EMPTY))) { + dev_err(master->dev, "MXS SPI: Timeout waiting for data\n"); + return -ETIMEDOUT; + } + + *data++ = readl(mxs->regs + HW_SSP_DATA) & 0xff; + } + + if (wait_on_timeout(MXS_SPI_MAX_TIMEOUT, + !(readl(mxs->regs + HW_SSP_CTRL0) & SSP_CTRL0_RUN))) { + dev_err(master->dev, "MXS SPI: Timeout waiting for finish\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int mxs_spi_transfer(struct spi_device *spi, struct spi_message *mesg) +{ + struct mxs_spi *mxs = to_mxs(spi->master); + struct spi_master *master = spi->master; + struct spi_transfer *t = NULL; + char dummy; + unsigned long flags = 0; + int write = 0; + char *data = NULL; + int ret; + mesg->actual_length = 0; + + list_for_each_entry(t, &mesg->transfers, transfer_list) { + flags = 0; + + if (t->tx_buf) { + data = (char *) t->tx_buf; + write = 1; + } else if (t->rx_buf) { + data = (char *) t->rx_buf; + write = 0; + } else if (t->rx_buf && t->tx_buf) { + dev_err(master->dev, "Cannot send and receive simultaneously\n"); + return -EIO; + } else if (!t->rx_buf && !t->tx_buf) { + dev_err(master->dev, "No Data\n"); + return -EIO; + } + + if (&t->transfer_list == mesg->transfers.next) + flags |= SPI_XFER_BEGIN; + + if (&t->transfer_list == mesg->transfers.prev) + flags |= SPI_XFER_END; + + if (t->len == 0) { + if (flags == SPI_XFER_END) { + t->len = 1; + t->rx_buf = (void *) &dummy; + } else { + return 0; + } + } + + writel(SSP_CTRL1_DMA_ENABLE, mxs->regs + HW_SSP_CTRL1 + BIT_CLR); + ret = mxs_spi_xfer_pio(spi, data, t->len, write, flags); + if (ret < 0) + return ret; + mesg->actual_length += t->len; + } + + return 0; +} + +static int mxs_spi_probe(struct device_d *dev) +{ + struct spi_master *master; + struct mxs_spi *mxs; + + mxs = xzalloc(sizeof(*mxs)); + + master = &mxs->master; + master->dev = dev; + + master->bus_num = dev->id; + master->setup = mxs_spi_setup; + master->transfer = mxs_spi_transfer; + master->num_chipselect = 3; + mxs->mode = SPI_CPOL | SPI_CPHA; + + mxs->regs = dev_request_mem_region(dev, 0); + + spi_register_master(master); + + return 0; +} + +static struct driver_d mxs_spi_driver = { + .name = "mxs_spi", + .probe = mxs_spi_probe, +}; + +static int __init mxs_spi_init(void) +{ + return platform_driver_register(&mxs_spi_driver); +} + +device_initcall(mxs_spi_init); + +MODULE_AUTHOR("Denx Software Engeneering and Michael Grzeschik"); +MODULE_DESCRIPTION("MXS SPI driver"); +MODULE_LICENSE("GPL"); |