* [PATCH 0/3] Add mxs spi driver and mx28evk as its first user @ 2013-02-04 16:26 Michael Grzeschik 2013-02-04 16:26 ` [PATCH 1/3] mxs: ssp move to common register layout Michael Grzeschik ` (2 more replies) 0 siblings, 3 replies; 10+ messages in thread From: Michael Grzeschik @ 2013-02-04 16:26 UTC (permalink / raw) To: barebox this series adds support for the mxs spi driver and adds the mx28evk as the first user to use it with an m25p80 flash memory. The driver was tested to work with the pio mode, as its implementation has currently no dma support. The SSP clock needed to be lower than the max possible frequency for the flash. Otherwise it sometimes, after a lot of spi transactions, the SSP stopped working. Michael Grzeschik (3): mxs: ssp move to common register layout mxs_spi: initial commit mx28evk: add spi arch/arm/boards/freescale-mx28-evk/mx28-evk.c | 28 +++ arch/arm/mach-mxs/include/mach/imx23-regs.h | 16 ++ arch/arm/mach-mxs/include/mach/imx28-regs.h | 21 ++ arch/arm/mach-mxs/include/mach/ssp.h | 79 +++++++ drivers/mci/mxs.c | 118 +--------- drivers/spi/Kconfig | 5 + drivers/spi/Makefile | 1 + drivers/spi/mxs_spi.c | 306 +++++++++++++++++++++++++ 8 files changed, 457 insertions(+), 117 deletions(-) create mode 100644 arch/arm/mach-mxs/include/mach/ssp.h create mode 100644 drivers/spi/mxs_spi.c -- 1.7.10.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 1/3] mxs: ssp move to common register layout 2013-02-04 16:26 [PATCH 0/3] Add mxs spi driver and mx28evk as its first user Michael Grzeschik @ 2013-02-04 16:26 ` Michael Grzeschik 2013-02-04 16:26 ` [PATCH 2/3] mxs_spi: initial commit Michael Grzeschik 2013-02-04 16:26 ` Michael Grzeschik 2 siblings, 0 replies; 10+ messages in thread From: Michael Grzeschik @ 2013-02-04 16:26 UTC (permalink / raw) To: barebox Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> --- arch/arm/mach-mxs/include/mach/imx23-regs.h | 16 ++++ arch/arm/mach-mxs/include/mach/imx28-regs.h | 21 +++++ arch/arm/mach-mxs/include/mach/ssp.h | 77 +++++++++++++++++ drivers/mci/mxs.c | 118 +-------------------------- 4 files changed, 115 insertions(+), 117 deletions(-) create mode 100644 arch/arm/mach-mxs/include/mach/ssp.h diff --git a/arch/arm/mach-mxs/include/mach/imx23-regs.h b/arch/arm/mach-mxs/include/mach/imx23-regs.h index 7fb664b..9e8e86c 100644 --- a/arch/arm/mach-mxs/include/mach/imx23-regs.h +++ b/arch/arm/mach-mxs/include/mach/imx23-regs.h @@ -36,4 +36,20 @@ #define IMX_DIGCTL_BASE 0x8001c000 #define IMX_USB_BASE 0x80080000 +#define HW_SSP_CTRL0 0x000 +#define HW_SSP_CMD0 0x010 +#define HW_SSP_CMD1 0x020 +#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 +#define HW_SSP_SDRESP0 0x080 +#define HW_SSP_SDRESP1 0x090 +#define HW_SSP_SDRESP2 0x0A0 +#define HW_SSP_SDRESP3 0x0B0 +#define HW_SSP_STATUS 0x0C0 +#define HW_SSP_DEBUG 0x100 +#define HW_SSP_VERSION 0x110 + #endif /* __ASM_ARCH_MX23_REGS_H */ diff --git a/arch/arm/mach-mxs/include/mach/imx28-regs.h b/arch/arm/mach-mxs/include/mach/imx28-regs.h index fa8edd7..bbe1848 100644 --- a/arch/arm/mach-mxs/include/mach/imx28-regs.h +++ b/arch/arm/mach-mxs/include/mach/imx28-regs.h @@ -45,4 +45,25 @@ #define IMX_FEC0_BASE 0x800F0000 #define IMX_FEC1_BASE 0x800F4000 +#define HW_SSP_CTRL0 0x000 +#define HW_SSP_CMD0 0x010 +#define HW_SSP_CMD1 0x020 +#define HW_SSP_XFER_COUNT 0x030 +#define HW_SSP_BLOCK_SIZE 0x040 +#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 +#define HW_SSP_SDRESP0 0x0A0 +#define HW_SSP_SDRESP1 0x0B0 +#define HW_SSP_SDRESP2 0x0C0 +#define HW_SSP_SDRESP3 0x0D0 +#define HW_SSP_DDR_CTRL 0x0E0 +#define HW_SSP_DLL_CTRL 0x0F0 +#define HW_SSP_STATUS 0x100 +#define HW_SSP_DLL_STS 0x110 +#define HW_SSP_DEBUG 0x120 +#define HW_SSP_VERSION 0x130 + #endif /* __ASM_ARCH_MX28_REGS_H */ diff --git a/arch/arm/mach-mxs/include/mach/ssp.h b/arch/arm/mach-mxs/include/mach/ssp.h new file mode 100644 index 0000000..f91770f --- /dev/null +++ b/arch/arm/mach-mxs/include/mach/ssp.h @@ -0,0 +1,77 @@ +/* + * Freescale MXS SSP + * + * Copyright (C) 2013 Michael Grzeschik <mgr@pengutronix.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + */ + +#ifndef __SSP_H__ +#define __SSP_H__ + +#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_WAIT_FOR_CMD (1 << 20) +#define SSP_CTRL0_LONG_RESP (1 << 19) +#define SSP_CTRL0_GET_RESP (1 << 17) +#define SSP_CTRL0_ENABLE (1 << 16) + +#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_CTRL0_XFER_COUNT(x) ((x) & 0xffff) +# 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) + +#ifdef CONFIG_ARCH_IMX28 +# define SSP_BLOCK_SIZE(x) ((x) & 0xf) +# define SSP_BLOCK_COUNT(x) (((x) & 0xffffff) << 4) +#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_PHASE (1 << 10) +#define SSP_CTRL1_WORD_LENGTH(x) (((x) & 0xf) << 4) +#define SSP_CTRL1_SSP_MODE(x) ((x) & 0xf) + +/* 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) + +#endif /* __SSP_H__ */ diff --git a/drivers/mci/mxs.c b/drivers/mci/mxs.c index ed644d1..5bdcd1b 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; -- 1.7.10.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 2/3] mxs_spi: initial commit 2013-02-04 16:26 [PATCH 0/3] Add mxs spi driver and mx28evk as its first user Michael Grzeschik 2013-02-04 16:26 ` [PATCH 1/3] mxs: ssp move to common register layout Michael Grzeschik @ 2013-02-04 16:26 ` Michael Grzeschik 2013-02-04 16:26 ` Michael Grzeschik 2 siblings, 0 replies; 10+ messages in thread From: Michael Grzeschik @ 2013-02-04 16:26 UTC (permalink / raw) To: barebox Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> --- arch/arm/mach-mxs/include/mach/ssp.h | 2 + drivers/spi/Kconfig | 5 + drivers/spi/Makefile | 1 + drivers/spi/mxs_spi.c | 306 ++++++++++++++++++++++++++++++++++ 4 files changed, 314 insertions(+) create mode 100644 drivers/spi/mxs_spi.c diff --git a/arch/arm/mach-mxs/include/mach/ssp.h b/arch/arm/mach-mxs/include/mach/ssp.h index f91770f..067cf12 100644 --- a/arch/arm/mach-mxs/include/mach/ssp.h +++ b/arch/arm/mach-mxs/include/mach/ssp.h @@ -23,6 +23,7 @@ #define SSP_CTRL0_BUS_WIDTH(x) (((x) & 0x3) << 22) #define SSP_CTRL0_WAIT_FOR_IRQ (1 << 21) #define SSP_CTRL0_WAIT_FOR_CMD (1 << 20) +#define SSP_CTRL0_SSP_ASSERT_OUT(x) (((x) & 0x3) << 20) #define SSP_CTRL0_LONG_RESP (1 << 19) #define SSP_CTRL0_GET_RESP (1 << 17) #define SSP_CTRL0_ENABLE (1 << 16) @@ -51,6 +52,7 @@ /* bit definition for register HW_SSP_CTRL1 */ #define SSP_CTRL1_POLARITY (1 << 9) #define SSP_CTRL1_PHASE (1 << 10) +#define SSP_CTRL1_DMA_ENABLE (1 << 13) #define SSP_CTRL1_WORD_LENGTH(x) (((x) & 0xf) << 4) #define SSP_CTRL1_SSP_MODE(x) ((x) & 0xf) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 10b8fea..218c2ff 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_MXS + 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 b53061e..642b7ec 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 0000000..6fb7f9c --- /dev/null +++ b/drivers/spi/mxs_spi.c @@ -0,0 +1,306 @@ +/* + * 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); + + debug("SPI%d: Set freq rate to %d KHz (requested %d KHz)\n", + bus, 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) { + printf("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 uint32_t mxs_spi_cs_to_reg(unsigned cs) +{ + uint32_t select = 0; + + if (cs & 1) + select |= SSP_CTRL0_WAIT_FOR_CMD; + if (cs & 2) + select |= SSP_CTRL0_WAIT_FOR_IRQ; + + return select; +} + +static void mxs_spi_set_cs(struct spi_device *spi) +{ + const uint32_t mask = SSP_CTRL0_WAIT_FOR_CMD | SSP_CTRL0_WAIT_FOR_IRQ; + uint32_t select; + struct mxs_spi *mxs = to_mxs(spi->master); + + writel(mask, mxs->regs + HW_SSP_CTRL0 + BIT_CLR); + select = mxs_spi_cs_to_reg(spi->chip_select); + 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); + + 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)) { + printf("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))) { + printf("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))) { + printf("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_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; + } + + if (&t->transfer_list == mesg->transfers.next) + flags |= SPI_XFER_BEGIN; + + if (&t->transfer_list == mesg->transfers.prev) + flags |= SPI_XFER_END; + + if ((t->rx_buf && t->tx_buf)) { + printf("Cannot send and receive simultaneously \n"); + break; + } + + if ((!t->rx_buf && !t->tx_buf)) { + printf("No Data\n"); + break; + } + + 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); + 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)); + if (!mxs) + return -ENOMEM; + + 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) +{ + platform_driver_register(&mxs_spi_driver); + return 0; +} + +device_initcall(mxs_spi_init); + +MODULE_AUTHOR("Denx Software Engeneering and Michael Grzeschik"); +MODULE_DESCRIPTION("MXS SPI driver"); +MODULE_LICENSE("GPL"); -- 1.7.10.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 2/3] mxs_spi: initial commit 2013-02-04 16:26 [PATCH 0/3] Add mxs spi driver and mx28evk as its first user Michael Grzeschik 2013-02-04 16:26 ` [PATCH 1/3] mxs: ssp move to common register layout Michael Grzeschik 2013-02-04 16:26 ` [PATCH 2/3] mxs_spi: initial commit Michael Grzeschik @ 2013-02-04 16:26 ` Michael Grzeschik 2013-02-04 17:10 ` Michael Grzeschik 2 siblings, 1 reply; 10+ messages in thread From: Michael Grzeschik @ 2013-02-04 16:26 UTC (permalink / raw) To: barebox Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> --- arch/arm/boards/freescale-mx28-evk/mx28-evk.c | 28 +++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/arch/arm/boards/freescale-mx28-evk/mx28-evk.c b/arch/arm/boards/freescale-mx28-evk/mx28-evk.c index 4311473..325365b 100644 --- a/arch/arm/boards/freescale-mx28-evk/mx28-evk.c +++ b/arch/arm/boards/freescale-mx28-evk/mx28-evk.c @@ -30,6 +30,7 @@ #include <mach/mci.h> #include <mach/fb.h> #include <mach/ocotp.h> +#include <spi/spi.h> #include <asm/armlinux.h> #include <asm/mmu.h> @@ -126,6 +127,13 @@ static const uint32_t mx28evk_pads[] = { GPMI_ALE | VE_3_3V, GPMI_CLE | VE_3_3V, GPMI_RESETN, /* act as WP, external PU */ + + /* SSP */ + SSP2_D0 | VE_3_3V | PULLUP(1) | STRENGTH(S8MA), /* MISO DO */ + SSP2_D3 | VE_3_3V | PULLUP(1) | STRENGTH(S8MA), /* SS0 !CS */ + SSP2_CMD | VE_3_3V | PULLUP(1) | STRENGTH(S8MA), /* MOSI DIO */ + SSP2_SCK | VE_3_3V | PULLUP(1) | STRENGTH(S8MA), /* CLK */ + }; static struct mxs_mci_platform_data mci_pdata = { @@ -225,6 +233,17 @@ static int mx28_evk_mem_init(void) } mem_initcall(mx28_evk_mem_init); +static const struct spi_board_info mx28evk_spi_board_info[] = { + { + .name = "m25p80", + /* we leave this with the lower frequency + as the ssp unit otherwise locks up */ + .max_speed_hz = 32000000, + .bus_num = 2, + .chip_select = 0, + } +}; + static int mx28_evk_devices_init(void) { int i; @@ -235,8 +254,11 @@ static int mx28_evk_devices_init(void) /* enable IOCLK0 to run at the PLL frequency */ imx_set_ioclk(0, 480000000); + imx_set_ioclk(1, 320000000); /* run the SSP unit clock at 100 MHz */ imx_set_sspclk(0, 100000000, 1); + /* run the SSP unit 2 clock at 160Mhz */ + imx_set_sspclk(2, 160000000, 1); armlinux_set_bootparams((void *)IMX_MEMORY_BASE + 0x100); armlinux_set_architecture(MACH_TYPE_MX28EVK); @@ -259,6 +281,12 @@ static int mx28_evk_devices_init(void) add_generic_device("mxs_nand", 0, NULL, MXS_GPMI_BASE, 0x2000, IORESOURCE_MEM, NULL); + spi_register_board_info(mx28evk_spi_board_info, + ARRAY_SIZE(mx28evk_spi_board_info)); + + add_generic_device("mxs_spi", 2, NULL, IMX_SSP2_BASE, 0x2000, + IORESOURCE_MEM, NULL); + return 0; } device_initcall(mx28_evk_devices_init); -- 1.7.10.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] mxs_spi: initial commit 2013-02-04 16:26 ` Michael Grzeschik @ 2013-02-04 17:10 ` Michael Grzeschik 0 siblings, 0 replies; 10+ messages in thread From: Michael Grzeschik @ 2013-02-04 17:10 UTC (permalink / raw) To: Michael Grzeschik; +Cc: barebox Somehow this patch doesn't have the corrent subject. I will resend. Thanks. On Mon, Feb 04, 2013 at 05:26:19PM +0100, Michael Grzeschik wrote: > Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> > --- > arch/arm/boards/freescale-mx28-evk/mx28-evk.c | 28 +++++++++++++++++++++++++ > 1 file changed, 28 insertions(+) > > diff --git a/arch/arm/boards/freescale-mx28-evk/mx28-evk.c b/arch/arm/boards/freescale-mx28-evk/mx28-evk.c > index 4311473..325365b 100644 > --- a/arch/arm/boards/freescale-mx28-evk/mx28-evk.c > +++ b/arch/arm/boards/freescale-mx28-evk/mx28-evk.c > @@ -30,6 +30,7 @@ > #include <mach/mci.h> > #include <mach/fb.h> > #include <mach/ocotp.h> > +#include <spi/spi.h> > > #include <asm/armlinux.h> > #include <asm/mmu.h> > @@ -126,6 +127,13 @@ static const uint32_t mx28evk_pads[] = { > GPMI_ALE | VE_3_3V, > GPMI_CLE | VE_3_3V, > GPMI_RESETN, /* act as WP, external PU */ > + > + /* SSP */ > + SSP2_D0 | VE_3_3V | PULLUP(1) | STRENGTH(S8MA), /* MISO DO */ > + SSP2_D3 | VE_3_3V | PULLUP(1) | STRENGTH(S8MA), /* SS0 !CS */ > + SSP2_CMD | VE_3_3V | PULLUP(1) | STRENGTH(S8MA), /* MOSI DIO */ > + SSP2_SCK | VE_3_3V | PULLUP(1) | STRENGTH(S8MA), /* CLK */ > + > }; > > static struct mxs_mci_platform_data mci_pdata = { > @@ -225,6 +233,17 @@ static int mx28_evk_mem_init(void) > } > mem_initcall(mx28_evk_mem_init); > > +static const struct spi_board_info mx28evk_spi_board_info[] = { > + { > + .name = "m25p80", > + /* we leave this with the lower frequency > + as the ssp unit otherwise locks up */ > + .max_speed_hz = 32000000, > + .bus_num = 2, > + .chip_select = 0, > + } > +}; > + > static int mx28_evk_devices_init(void) > { > int i; > @@ -235,8 +254,11 @@ static int mx28_evk_devices_init(void) > > /* enable IOCLK0 to run at the PLL frequency */ > imx_set_ioclk(0, 480000000); > + imx_set_ioclk(1, 320000000); > /* run the SSP unit clock at 100 MHz */ > imx_set_sspclk(0, 100000000, 1); > + /* run the SSP unit 2 clock at 160Mhz */ > + imx_set_sspclk(2, 160000000, 1); > > armlinux_set_bootparams((void *)IMX_MEMORY_BASE + 0x100); > armlinux_set_architecture(MACH_TYPE_MX28EVK); > @@ -259,6 +281,12 @@ static int mx28_evk_devices_init(void) > add_generic_device("mxs_nand", 0, NULL, MXS_GPMI_BASE, 0x2000, > IORESOURCE_MEM, NULL); > > + spi_register_board_info(mx28evk_spi_board_info, > + ARRAY_SIZE(mx28evk_spi_board_info)); > + > + add_generic_device("mxs_spi", 2, NULL, IMX_SSP2_BASE, 0x2000, > + IORESOURCE_MEM, NULL); > + > return 0; > } > device_initcall(mx28_evk_devices_init); > -- > 1.7.10.4 > > > _______________________________________________ > barebox mailing list > barebox@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/barebox > -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 0/3] Add mxs spi driver and mx28evk as its first user @ 2013-02-04 17:26 Michael Grzeschik 2013-02-04 17:26 ` [PATCH 2/3] mxs_spi: initial commit Michael Grzeschik 0 siblings, 1 reply; 10+ messages in thread From: Michael Grzeschik @ 2013-02-04 17:26 UTC (permalink / raw) To: barebox this series adds support for the mxs spi driver and adds the mx28evk as the first user to use it with an m25p80 flash memory. The driver was tested to work with the pio mode, as its implementation has currently no dma support. The SSP clock needed to be lower than the max possible frequency for the flash. Otherwise it sometimes, after a lot of spi transactions, the SSP stopped working. Michael Grzeschik (3): mxs: ssp move to common register layout mxs_spi: initial commit mx28evk: add m25p80 flash via ssp2 arch/arm/boards/freescale-mx28-evk/mx28-evk.c | 28 +++ arch/arm/mach-mxs/include/mach/imx23-regs.h | 16 ++ arch/arm/mach-mxs/include/mach/imx28-regs.h | 21 ++ arch/arm/mach-mxs/include/mach/ssp.h | 79 +++++++ drivers/mci/mxs.c | 118 +--------- drivers/spi/Kconfig | 5 + drivers/spi/Makefile | 1 + drivers/spi/mxs_spi.c | 306 +++++++++++++++++++++++++ 8 files changed, 457 insertions(+), 117 deletions(-) create mode 100644 arch/arm/mach-mxs/include/mach/ssp.h create mode 100644 drivers/spi/mxs_spi.c -- 1.7.10.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 2/3] mxs_spi: initial commit 2013-02-04 17:26 [PATCH 0/3] Add mxs spi driver and mx28evk as its first user Michael Grzeschik @ 2013-02-04 17:26 ` Michael Grzeschik 2013-02-04 17:57 ` Alexander Aring 2013-02-05 9:22 ` Sascha Hauer 0 siblings, 2 replies; 10+ messages in thread From: Michael Grzeschik @ 2013-02-04 17:26 UTC (permalink / raw) To: barebox Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> --- arch/arm/mach-mxs/include/mach/ssp.h | 2 + drivers/spi/Kconfig | 5 + drivers/spi/Makefile | 1 + drivers/spi/mxs_spi.c | 306 ++++++++++++++++++++++++++++++++++ 4 files changed, 314 insertions(+) create mode 100644 drivers/spi/mxs_spi.c diff --git a/arch/arm/mach-mxs/include/mach/ssp.h b/arch/arm/mach-mxs/include/mach/ssp.h index f91770f..067cf12 100644 --- a/arch/arm/mach-mxs/include/mach/ssp.h +++ b/arch/arm/mach-mxs/include/mach/ssp.h @@ -23,6 +23,7 @@ #define SSP_CTRL0_BUS_WIDTH(x) (((x) & 0x3) << 22) #define SSP_CTRL0_WAIT_FOR_IRQ (1 << 21) #define SSP_CTRL0_WAIT_FOR_CMD (1 << 20) +#define SSP_CTRL0_SSP_ASSERT_OUT(x) (((x) & 0x3) << 20) #define SSP_CTRL0_LONG_RESP (1 << 19) #define SSP_CTRL0_GET_RESP (1 << 17) #define SSP_CTRL0_ENABLE (1 << 16) @@ -51,6 +52,7 @@ /* bit definition for register HW_SSP_CTRL1 */ #define SSP_CTRL1_POLARITY (1 << 9) #define SSP_CTRL1_PHASE (1 << 10) +#define SSP_CTRL1_DMA_ENABLE (1 << 13) #define SSP_CTRL1_WORD_LENGTH(x) (((x) & 0xf) << 4) #define SSP_CTRL1_SSP_MODE(x) ((x) & 0xf) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 10b8fea..218c2ff 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_MXS + 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 b53061e..642b7ec 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 0000000..6fb7f9c --- /dev/null +++ b/drivers/spi/mxs_spi.c @@ -0,0 +1,306 @@ +/* + * 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); + + debug("SPI%d: Set freq rate to %d KHz (requested %d KHz)\n", + bus, 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) { + printf("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 uint32_t mxs_spi_cs_to_reg(unsigned cs) +{ + uint32_t select = 0; + + if (cs & 1) + select |= SSP_CTRL0_WAIT_FOR_CMD; + if (cs & 2) + select |= SSP_CTRL0_WAIT_FOR_IRQ; + + return select; +} + +static void mxs_spi_set_cs(struct spi_device *spi) +{ + const uint32_t mask = SSP_CTRL0_WAIT_FOR_CMD | SSP_CTRL0_WAIT_FOR_IRQ; + uint32_t select; + struct mxs_spi *mxs = to_mxs(spi->master); + + writel(mask, mxs->regs + HW_SSP_CTRL0 + BIT_CLR); + select = mxs_spi_cs_to_reg(spi->chip_select); + 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); + + 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)) { + printf("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))) { + printf("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))) { + printf("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_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; + } + + if (&t->transfer_list == mesg->transfers.next) + flags |= SPI_XFER_BEGIN; + + if (&t->transfer_list == mesg->transfers.prev) + flags |= SPI_XFER_END; + + if ((t->rx_buf && t->tx_buf)) { + printf("Cannot send and receive simultaneously \n"); + break; + } + + if ((!t->rx_buf && !t->tx_buf)) { + printf("No Data\n"); + break; + } + + 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); + 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)); + if (!mxs) + return -ENOMEM; + + 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) +{ + platform_driver_register(&mxs_spi_driver); + return 0; +} + +device_initcall(mxs_spi_init); + +MODULE_AUTHOR("Denx Software Engeneering and Michael Grzeschik"); +MODULE_DESCRIPTION("MXS SPI driver"); +MODULE_LICENSE("GPL"); -- 1.7.10.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] mxs_spi: initial commit 2013-02-04 17:26 ` [PATCH 2/3] mxs_spi: initial commit Michael Grzeschik @ 2013-02-04 17:57 ` Alexander Aring 2013-02-04 18:15 ` Alexander Aring 2013-02-05 9:22 ` Sascha Hauer 1 sibling, 1 reply; 10+ messages in thread From: Alexander Aring @ 2013-02-04 17:57 UTC (permalink / raw) To: Michael Grzeschik; +Cc: barebox Hi Michael, On Mon, Feb 04, 2013 at 06:26:20PM +0100, Michael Grzeschik wrote: > Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> > --- > arch/arm/mach-mxs/include/mach/ssp.h | 2 + > drivers/spi/Kconfig | 5 + > drivers/spi/Makefile | 1 + > drivers/spi/mxs_spi.c | 306 ++++++++++++++++++++++++++++++++++ > 4 files changed, 314 insertions(+) > create mode 100644 drivers/spi/mxs_spi.c > > diff --git a/arch/arm/mach-mxs/include/mach/ssp.h b/arch/arm/mach-mxs/include/mach/ssp.h > index f91770f..067cf12 100644 > --- a/arch/arm/mach-mxs/include/mach/ssp.h > +++ b/arch/arm/mach-mxs/include/mach/ssp.h > @@ -23,6 +23,7 @@ > #define SSP_CTRL0_BUS_WIDTH(x) (((x) & 0x3) << 22) > #define SSP_CTRL0_WAIT_FOR_IRQ (1 << 21) > #define SSP_CTRL0_WAIT_FOR_CMD (1 << 20) > +#define SSP_CTRL0_SSP_ASSERT_OUT(x) (((x) & 0x3) << 20) > #define SSP_CTRL0_LONG_RESP (1 << 19) > #define SSP_CTRL0_GET_RESP (1 << 17) > #define SSP_CTRL0_ENABLE (1 << 16) > @@ -51,6 +52,7 @@ > /* bit definition for register HW_SSP_CTRL1 */ > #define SSP_CTRL1_POLARITY (1 << 9) > #define SSP_CTRL1_PHASE (1 << 10) > +#define SSP_CTRL1_DMA_ENABLE (1 << 13) > #define SSP_CTRL1_WORD_LENGTH(x) (((x) & 0xf) << 4) > #define SSP_CTRL1_SSP_MODE(x) ((x) & 0xf) > > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig > index 10b8fea..218c2ff 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_MXS > + 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 b53061e..642b7ec 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 0000000..6fb7f9c > --- /dev/null > +++ b/drivers/spi/mxs_spi.c > @@ -0,0 +1,306 @@ > +/* > + * 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); > + > + debug("SPI%d: Set freq rate to %d KHz (requested %d KHz)\n", > + bus, 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) { > + printf("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 uint32_t mxs_spi_cs_to_reg(unsigned cs) > +{ > + uint32_t select = 0; > + > + if (cs & 1) > + select |= SSP_CTRL0_WAIT_FOR_CMD; > + if (cs & 2) > + select |= SSP_CTRL0_WAIT_FOR_IRQ; > + > + return select; > +} > + > +static void mxs_spi_set_cs(struct spi_device *spi) > +{ > + const uint32_t mask = SSP_CTRL0_WAIT_FOR_CMD | SSP_CTRL0_WAIT_FOR_IRQ; > + uint32_t select; > + struct mxs_spi *mxs = to_mxs(spi->master); > + > + writel(mask, mxs->regs + HW_SSP_CTRL0 + BIT_CLR); > + select = mxs_spi_cs_to_reg(spi->chip_select); > + 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); > + > + 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)) { > + printf("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))) { > + printf("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))) { > + printf("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_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) { > + Maybe we should remove this whitespace newline. > + 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; > + } > + > + if (&t->transfer_list == mesg->transfers.next) > + flags |= SPI_XFER_BEGIN; > + > + if (&t->transfer_list == mesg->transfers.prev) > + flags |= SPI_XFER_END; > + > + if ((t->rx_buf && t->tx_buf)) { > + printf("Cannot send and receive simultaneously \n"); > + break; Maybe we should return -EIO or something here with variable ret. > + } > + > + if ((!t->rx_buf && !t->tx_buf)) { > + printf("No Data\n"); > + break; Same here. > + } > + > + 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); ret is never read in this function after that. Regards Alex > + 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)); > + if (!mxs) > + return -ENOMEM; > + > + 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) > +{ > + platform_driver_register(&mxs_spi_driver); > + return 0; > +} > + > +device_initcall(mxs_spi_init); > + > +MODULE_AUTHOR("Denx Software Engeneering and Michael Grzeschik"); > +MODULE_DESCRIPTION("MXS SPI driver"); > +MODULE_LICENSE("GPL"); > -- > 1.7.10.4 > > > _______________________________________________ > barebox mailing list > barebox@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/barebox _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] mxs_spi: initial commit 2013-02-04 17:57 ` Alexander Aring @ 2013-02-04 18:15 ` Alexander Aring 2013-02-04 18:18 ` Alexander Aring 0 siblings, 1 reply; 10+ messages in thread From: Alexander Aring @ 2013-02-04 18:15 UTC (permalink / raw) To: Michael Grzeschik; +Cc: barebox Hi, found little thing. On Mon, Feb 04, 2013 at 06:57:42PM +0100, Alexander Aring wrote: > Hi Michael, > > On Mon, Feb 04, 2013 at 06:26:20PM +0100, Michael Grzeschik wrote: > > Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> > > --- > > arch/arm/mach-mxs/include/mach/ssp.h | 2 + > > drivers/spi/Kconfig | 5 + > > drivers/spi/Makefile | 1 + > > drivers/spi/mxs_spi.c | 306 ++++++++++++++++++++++++++++++++++ > > 4 files changed, 314 insertions(+) > > create mode 100644 drivers/spi/mxs_spi.c > > > > diff --git a/arch/arm/mach-mxs/include/mach/ssp.h b/arch/arm/mach-mxs/include/mach/ssp.h > > index f91770f..067cf12 100644 > > --- a/arch/arm/mach-mxs/include/mach/ssp.h > > +++ b/arch/arm/mach-mxs/include/mach/ssp.h > > @@ -23,6 +23,7 @@ > > #define SSP_CTRL0_BUS_WIDTH(x) (((x) & 0x3) << 22) > > #define SSP_CTRL0_WAIT_FOR_IRQ (1 << 21) > > #define SSP_CTRL0_WAIT_FOR_CMD (1 << 20) > > +#define SSP_CTRL0_SSP_ASSERT_OUT(x) (((x) & 0x3) << 20) > > #define SSP_CTRL0_LONG_RESP (1 << 19) > > #define SSP_CTRL0_GET_RESP (1 << 17) > > #define SSP_CTRL0_ENABLE (1 << 16) > > @@ -51,6 +52,7 @@ > > /* bit definition for register HW_SSP_CTRL1 */ > > #define SSP_CTRL1_POLARITY (1 << 9) > > #define SSP_CTRL1_PHASE (1 << 10) > > +#define SSP_CTRL1_DMA_ENABLE (1 << 13) > > #define SSP_CTRL1_WORD_LENGTH(x) (((x) & 0xf) << 4) > > #define SSP_CTRL1_SSP_MODE(x) ((x) & 0xf) > > > > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig > > index 10b8fea..218c2ff 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_MXS Can we use ARCH23 || ARCH_28 here instead of ARCH_MXS? I don't know if these two are inside of ARCH_MXS. Regards Alex > > + 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 b53061e..642b7ec 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 0000000..6fb7f9c > > --- /dev/null > > +++ b/drivers/spi/mxs_spi.c > > @@ -0,0 +1,306 @@ > > +/* > > + * 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); > > + > > + debug("SPI%d: Set freq rate to %d KHz (requested %d KHz)\n", > > + bus, 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) { > > + printf("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 uint32_t mxs_spi_cs_to_reg(unsigned cs) > > +{ > > + uint32_t select = 0; > > + > > + if (cs & 1) > > + select |= SSP_CTRL0_WAIT_FOR_CMD; > > + if (cs & 2) > > + select |= SSP_CTRL0_WAIT_FOR_IRQ; > > + > > + return select; > > +} > > + > > +static void mxs_spi_set_cs(struct spi_device *spi) > > +{ > > + const uint32_t mask = SSP_CTRL0_WAIT_FOR_CMD | SSP_CTRL0_WAIT_FOR_IRQ; > > + uint32_t select; > > + struct mxs_spi *mxs = to_mxs(spi->master); > > + > > + writel(mask, mxs->regs + HW_SSP_CTRL0 + BIT_CLR); > > + select = mxs_spi_cs_to_reg(spi->chip_select); > > + 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); > > + > > + 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)) { > > + printf("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))) { > > + printf("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))) { > > + printf("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_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) { > > + > > Maybe we should remove this whitespace newline. > > > + 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; > > + } > > + > > + if (&t->transfer_list == mesg->transfers.next) > > + flags |= SPI_XFER_BEGIN; > > + > > + if (&t->transfer_list == mesg->transfers.prev) > > + flags |= SPI_XFER_END; > > + > > + if ((t->rx_buf && t->tx_buf)) { > > + printf("Cannot send and receive simultaneously \n"); > > + break; > > Maybe we should return -EIO or something here with variable ret. > > > + } > > + > > + if ((!t->rx_buf && !t->tx_buf)) { > > + printf("No Data\n"); > > + break; > > Same here. > > > + } > > + > > + 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); > > ret is never read in this function after that. > > Regards Alex > > > + 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)); > > + if (!mxs) > > + return -ENOMEM; > > + > > + 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) > > +{ > > + platform_driver_register(&mxs_spi_driver); > > + return 0; > > +} > > + > > +device_initcall(mxs_spi_init); > > + > > +MODULE_AUTHOR("Denx Software Engeneering and Michael Grzeschik"); > > +MODULE_DESCRIPTION("MXS SPI driver"); > > +MODULE_LICENSE("GPL"); > > -- > > 1.7.10.4 > > > > > > _______________________________________________ > > barebox mailing list > > barebox@lists.infradead.org > > http://lists.infradead.org/mailman/listinfo/barebox _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] mxs_spi: initial commit 2013-02-04 18:15 ` Alexander Aring @ 2013-02-04 18:18 ` Alexander Aring 0 siblings, 0 replies; 10+ messages in thread From: Alexander Aring @ 2013-02-04 18:18 UTC (permalink / raw) To: Michael Grzeschik; +Cc: barebox Hi, On Mon, Feb 04, 2013 at 07:15:19PM +0100, Alexander Aring wrote: > Hi, > > found little thing. > > On Mon, Feb 04, 2013 at 06:57:42PM +0100, Alexander Aring wrote: > > Hi Michael, > > > > On Mon, Feb 04, 2013 at 06:26:20PM +0100, Michael Grzeschik wrote: > > > Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> > > > --- > > > arch/arm/mach-mxs/include/mach/ssp.h | 2 + > > > drivers/spi/Kconfig | 5 + > > > drivers/spi/Makefile | 1 + > > > drivers/spi/mxs_spi.c | 306 ++++++++++++++++++++++++++++++++++ > > > 4 files changed, 314 insertions(+) > > > create mode 100644 drivers/spi/mxs_spi.c > > > > > > diff --git a/arch/arm/mach-mxs/include/mach/ssp.h b/arch/arm/mach-mxs/include/mach/ssp.h > > > index f91770f..067cf12 100644 > > > --- a/arch/arm/mach-mxs/include/mach/ssp.h > > > +++ b/arch/arm/mach-mxs/include/mach/ssp.h > > > @@ -23,6 +23,7 @@ > > > #define SSP_CTRL0_BUS_WIDTH(x) (((x) & 0x3) << 22) > > > #define SSP_CTRL0_WAIT_FOR_IRQ (1 << 21) > > > #define SSP_CTRL0_WAIT_FOR_CMD (1 << 20) > > > +#define SSP_CTRL0_SSP_ASSERT_OUT(x) (((x) & 0x3) << 20) > > > #define SSP_CTRL0_LONG_RESP (1 << 19) > > > #define SSP_CTRL0_GET_RESP (1 << 17) > > > #define SSP_CTRL0_ENABLE (1 << 16) > > > @@ -51,6 +52,7 @@ > > > /* bit definition for register HW_SSP_CTRL1 */ > > > #define SSP_CTRL1_POLARITY (1 << 9) > > > #define SSP_CTRL1_PHASE (1 << 10) > > > +#define SSP_CTRL1_DMA_ENABLE (1 << 13) > > > #define SSP_CTRL1_WORD_LENGTH(x) (((x) & 0xf) << 4) > > > #define SSP_CTRL1_SSP_MODE(x) ((x) & 0xf) > > > > > > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig > > > index 10b8fea..218c2ff 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_MXS > > Can we use ARCH23 || ARCH_28 here instead of ARCH_MXS? > I don't know if these two are inside of ARCH_MXS. argl, I mean ARCH_IMX23 || ARCH_IMX28. Sorry about that. Regards Alex > > Regards Alex > > > > + 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 b53061e..642b7ec 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 0000000..6fb7f9c > > > --- /dev/null > > > +++ b/drivers/spi/mxs_spi.c > > > @@ -0,0 +1,306 @@ > > > +/* > > > + * 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); > > > + > > > + debug("SPI%d: Set freq rate to %d KHz (requested %d KHz)\n", > > > + bus, 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) { > > > + printf("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 uint32_t mxs_spi_cs_to_reg(unsigned cs) > > > +{ > > > + uint32_t select = 0; > > > + > > > + if (cs & 1) > > > + select |= SSP_CTRL0_WAIT_FOR_CMD; > > > + if (cs & 2) > > > + select |= SSP_CTRL0_WAIT_FOR_IRQ; > > > + > > > + return select; > > > +} > > > + > > > +static void mxs_spi_set_cs(struct spi_device *spi) > > > +{ > > > + const uint32_t mask = SSP_CTRL0_WAIT_FOR_CMD | SSP_CTRL0_WAIT_FOR_IRQ; > > > + uint32_t select; > > > + struct mxs_spi *mxs = to_mxs(spi->master); > > > + > > > + writel(mask, mxs->regs + HW_SSP_CTRL0 + BIT_CLR); > > > + select = mxs_spi_cs_to_reg(spi->chip_select); > > > + 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); > > > + > > > + 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)) { > > > + printf("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))) { > > > + printf("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))) { > > > + printf("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_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) { > > > + > > > > Maybe we should remove this whitespace newline. > > > > > + 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; > > > + } > > > + > > > + if (&t->transfer_list == mesg->transfers.next) > > > + flags |= SPI_XFER_BEGIN; > > > + > > > + if (&t->transfer_list == mesg->transfers.prev) > > > + flags |= SPI_XFER_END; > > > + > > > + if ((t->rx_buf && t->tx_buf)) { > > > + printf("Cannot send and receive simultaneously \n"); > > > + break; > > > > Maybe we should return -EIO or something here with variable ret. > > > > > + } > > > + > > > + if ((!t->rx_buf && !t->tx_buf)) { > > > + printf("No Data\n"); > > > + break; > > > > Same here. > > > > > + } > > > + > > > + 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); > > > > ret is never read in this function after that. > > > > Regards Alex > > > > > + 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)); > > > + if (!mxs) > > > + return -ENOMEM; > > > + > > > + 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) > > > +{ > > > + platform_driver_register(&mxs_spi_driver); > > > + return 0; > > > +} > > > + > > > +device_initcall(mxs_spi_init); > > > + > > > +MODULE_AUTHOR("Denx Software Engeneering and Michael Grzeschik"); > > > +MODULE_DESCRIPTION("MXS SPI driver"); > > > +MODULE_LICENSE("GPL"); > > > -- > > > 1.7.10.4 > > > > > > > > > _______________________________________________ > > > barebox mailing list > > > barebox@lists.infradead.org > > > http://lists.infradead.org/mailman/listinfo/barebox _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 2/3] mxs_spi: initial commit 2013-02-04 17:26 ` [PATCH 2/3] mxs_spi: initial commit Michael Grzeschik 2013-02-04 17:57 ` Alexander Aring @ 2013-02-05 9:22 ` Sascha Hauer 1 sibling, 0 replies; 10+ messages in thread From: Sascha Hauer @ 2013-02-05 9:22 UTC (permalink / raw) To: Michael Grzeschik; +Cc: barebox s/$subject/SPI: Add i.MX 23/28 SPI driver support/ On Mon, Feb 04, 2013 at 06:26:20PM +0100, Michael Grzeschik wrote: > + > +/* > + * 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); > + > + debug("SPI%d: Set freq rate to %d KHz (requested %d KHz)\n", > + bus, tgtclk, freq); Use dev_dbg, dev_info, dev_err throughout the driver please. > + > +static int mxs_spi_probe(struct device_d *dev) > +{ > + struct spi_master *master; > + struct mxs_spi *mxs; > + > + mxs = xzalloc(sizeof(*mxs)); > + if (!mxs) > + return -ENOMEM; xzalloc always returns memory. > + > + 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) > +{ > + platform_driver_register(&mxs_spi_driver); > + return 0; return platform_driver_register(&mxs_spi_driver); Sascha -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2013-02-05 9:22 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2013-02-04 16:26 [PATCH 0/3] Add mxs spi driver and mx28evk as its first user Michael Grzeschik 2013-02-04 16:26 ` [PATCH 1/3] mxs: ssp move to common register layout Michael Grzeschik 2013-02-04 16:26 ` [PATCH 2/3] mxs_spi: initial commit Michael Grzeschik 2013-02-04 16:26 ` Michael Grzeschik 2013-02-04 17:10 ` Michael Grzeschik 2013-02-04 17:26 [PATCH 0/3] Add mxs spi driver and mx28evk as its first user Michael Grzeschik 2013-02-04 17:26 ` [PATCH 2/3] mxs_spi: initial commit Michael Grzeschik 2013-02-04 17:57 ` Alexander Aring 2013-02-04 18:15 ` Alexander Aring 2013-02-04 18:18 ` Alexander Aring 2013-02-05 9:22 ` Sascha Hauer
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox