* [PATCH 1/2] ARM: mxs: Provide a common header file for SSP controller @ 2012-04-19 0:30 Fabio Estevam [not found] ` <1334795434-8780-1-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 0 siblings, 1 reply; 7+ messages in thread From: Fabio Estevam @ 2012-04-19 0:30 UTC (permalink / raw) To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f Cc: Fabio Estevam, shawn.guo-QSEj5FYQhm4dnm+yROfE0A, snijsure-4jo+YWezP1RWk0Htik3J/w, marek.vasut-Re5JQEeQqe8AvxtiuMwx3w, kernel-bIcnvbaLZ9MEGnE8C9+IrQ, Chris Ball On mxs SoCs the SSP controller can act as MMC or SPI controller. Remove the SSP related definitions from the mxs-mmc driver and put it on a common header file. This will facilitate the introduction of the spi-mxs driver. Cc: Chris Ball <cjb-2X9k7bc8m7Mdnm+yROfE0A@public.gmane.org> Signed-off-by: Fabio Estevam <fabio.estevam-KZfg59tc24xl57MIdRCFDg@public.gmane.org> --- arch/arm/mach-mxs/include/mach/ssp-regs.h | 114 +++++++++++++++++++++++++++++ drivers/mmc/host/mxs-mmc.c | 93 +----------------------- 2 files changed, 116 insertions(+), 91 deletions(-) create mode 100644 arch/arm/mach-mxs/include/mach/ssp-regs.h diff --git a/arch/arm/mach-mxs/include/mach/ssp-regs.h b/arch/arm/mach-mxs/include/mach/ssp-regs.h new file mode 100644 index 0000000..4bb0b27 --- /dev/null +++ b/arch/arm/mach-mxs/include/mach/ssp-regs.h @@ -0,0 +1,114 @@ +/* + * Freescale MXS SSP Registers + * + * Copyright 2012 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. + */ + +#ifndef __SSP_REGS_H +#define __SSP_REGS_H + + +/* card detect polling timeout */ +#define MXS_MMC_DETECT_TIMEOUT (HZ/2) + +#define SSP_VERSION_LATEST 4 +#define ssp_is_old() (rev_struct < SSP_VERSION_LATEST) + +/* SSP registers */ +#define HW_SSP_CTRL0 0x000 +#define BM_SSP_CTRL0_RUN (1 << 29) +#define BM_SSP_CTRL0_SDIO_IRQ_CHECK (1 << 28) +#define BM_SSP_CTRL0_IGNORE_CRC (1 << 26) +#define BM_SSP_CTRL0_READ (1 << 25) +#define BM_SSP_CTRL0_DATA_XFER (1 << 24) +#define BP_SSP_CTRL0_BUS_WIDTH 22 +#define BM_SSP_CTRL0_BUS_WIDTH (0x3 << 22) +#define BM_SSP_CTRL0_WAIT_FOR_IRQ (1 << 21) +#define BM_SSP_CTRL0_LONG_RESP (1 << 19) +#define BM_SSP_CTRL0_GET_RESP (1 << 17) +#define BM_SSP_CTRL0_ENABLE (1 << 16) +#define BP_SSP_CTRL0_XFER_COUNT 0 +#define BM_SSP_CTRL0_XFER_COUNT 0xffff +#define HW_SSP_CMD0 0x010 +#define BM_SSP_CMD0_DBL_DATA_RATE_EN (1 << 25) +#define BM_SSP_CMD0_SLOW_CLKING_EN (1 << 22) +#define BM_SSP_CMD0_CONT_CLKING_EN (1 << 21) +#define BM_SSP_CMD0_APPEND_8CYC (1 << 20) +#define BP_SSP_CMD0_BLOCK_SIZE 16 +#define BM_SSP_CMD0_BLOCK_SIZE (0xf << 16) +#define BP_SSP_CMD0_BLOCK_COUNT 8 +#define BM_SSP_CMD0_BLOCK_COUNT (0xff << 8) +#define BP_SSP_CMD0_CMD 0 +#define BM_SSP_CMD0_CMD 0xff +#define HW_SSP_CMD1 0x020 +#define HW_SSP_XFER_SIZE 0x030 +#define HW_SSP_BLOCK_SIZE 0x040 +#define BP_SSP_BLOCK_SIZE_BLOCK_COUNT 4 +#define BM_SSP_BLOCK_SIZE_BLOCK_COUNT (0xffffff << 4) +#define BP_SSP_BLOCK_SIZE_BLOCK_SIZE 0 +#define BM_SSP_BLOCK_SIZE_BLOCK_SIZE 0xf +#define HW_SSP_TIMING (ssp_is_old() ? 0x050 : 0x070) +#define BP_SSP_TIMING_TIMEOUT 16 +#define BM_SSP_TIMING_TIMEOUT (0xffff << 16) +#define BP_SSP_TIMING_CLOCK_DIVIDE 8 +#define BM_SSP_TIMING_CLOCK_DIVIDE (0xff << 8) +#define BP_SSP_TIMING_CLOCK_RATE 0 +#define BM_SSP_TIMING_CLOCK_RATE 0xff +#define HW_SSP_CTRL1 (ssp_is_old() ? 0x060 : 0x080) +#define BM_SSP_CTRL1_SDIO_IRQ (1 << 31) +#define BM_SSP_CTRL1_SDIO_IRQ_EN (1 << 30) +#define BM_SSP_CTRL1_RESP_ERR_IRQ (1 << 29) +#define BM_SSP_CTRL1_RESP_ERR_IRQ_EN (1 << 28) +#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ (1 << 27) +#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN (1 << 26) +#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ (1 << 25) +#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN (1 << 24) +#define BM_SSP_CTRL1_DATA_CRC_IRQ (1 << 23) +#define BM_SSP_CTRL1_DATA_CRC_IRQ_EN (1 << 22) +#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ (1 << 21) +#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ_EN (1 << 20) +#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ (1 << 17) +#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN (1 << 16) +#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ (1 << 15) +#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN (1 << 14) +#define BM_SSP_CTRL1_DMA_ENABLE (1 << 13) +#define BM_SSP_CTRL1_POLARITY (1 << 9) +#define BP_SSP_CTRL1_WORD_LENGTH 4 +#define BM_SSP_CTRL1_WORD_LENGTH (0xf << 4) +#define BP_SSP_CTRL1_SSP_MODE 0 +#define BM_SSP_CTRL1_SSP_MODE 0xf +#define HW_SSP_SDRESP0 (ssp_is_old() ? 0x080 : 0x0a0) +#define HW_SSP_SDRESP1 (ssp_is_old() ? 0x090 : 0x0b0) +#define HW_SSP_SDRESP2 (ssp_is_old() ? 0x0a0 : 0x0c0) +#define HW_SSP_SDRESP3 (ssp_is_old() ? 0x0b0 : 0x0d0) +#define HW_SSP_STATUS (ssp_is_old() ? 0x0c0 : 0x100) +#define BM_SSP_STATUS_CARD_DETECT (1 << 28) +#define BM_SSP_STATUS_SDIO_IRQ (1 << 17) +#define HW_SSP_VERSION (cpu_is_mx23() ? 0x110 : 0x130) +#define BP_SSP_VERSION_MAJOR 24 + +#define BF_SSP(value, field) (((value) << BP_SSP_##field) & BM_SSP_##field) + +#define MXS_MMC_IRQ_BITS (BM_SSP_CTRL1_SDIO_IRQ | \ + BM_SSP_CTRL1_RESP_ERR_IRQ | \ + BM_SSP_CTRL1_RESP_TIMEOUT_IRQ | \ + BM_SSP_CTRL1_DATA_TIMEOUT_IRQ | \ + BM_SSP_CTRL1_DATA_CRC_IRQ | \ + BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ | \ + BM_SSP_CTRL1_RECV_TIMEOUT_IRQ | \ + BM_SSP_CTRL1_FIFO_OVERRUN_IRQ) + +#define SSP_PIO_NUM 3 + +#endif /* __SSP_REGS_H */ diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index b0f2ef9..44d19ef 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -43,100 +43,11 @@ #include <mach/mxs.h> #include <mach/common.h> #include <mach/mmc.h> +#include <mach/ssp-regs.h> #define DRIVER_NAME "mxs-mmc" -/* card detect polling timeout */ -#define MXS_MMC_DETECT_TIMEOUT (HZ/2) - -#define SSP_VERSION_LATEST 4 -#define ssp_is_old() (host->version < SSP_VERSION_LATEST) - -/* SSP registers */ -#define HW_SSP_CTRL0 0x000 -#define BM_SSP_CTRL0_RUN (1 << 29) -#define BM_SSP_CTRL0_SDIO_IRQ_CHECK (1 << 28) -#define BM_SSP_CTRL0_IGNORE_CRC (1 << 26) -#define BM_SSP_CTRL0_READ (1 << 25) -#define BM_SSP_CTRL0_DATA_XFER (1 << 24) -#define BP_SSP_CTRL0_BUS_WIDTH (22) -#define BM_SSP_CTRL0_BUS_WIDTH (0x3 << 22) -#define BM_SSP_CTRL0_WAIT_FOR_IRQ (1 << 21) -#define BM_SSP_CTRL0_LONG_RESP (1 << 19) -#define BM_SSP_CTRL0_GET_RESP (1 << 17) -#define BM_SSP_CTRL0_ENABLE (1 << 16) -#define BP_SSP_CTRL0_XFER_COUNT (0) -#define BM_SSP_CTRL0_XFER_COUNT (0xffff) -#define HW_SSP_CMD0 0x010 -#define BM_SSP_CMD0_DBL_DATA_RATE_EN (1 << 25) -#define BM_SSP_CMD0_SLOW_CLKING_EN (1 << 22) -#define BM_SSP_CMD0_CONT_CLKING_EN (1 << 21) -#define BM_SSP_CMD0_APPEND_8CYC (1 << 20) -#define BP_SSP_CMD0_BLOCK_SIZE (16) -#define BM_SSP_CMD0_BLOCK_SIZE (0xf << 16) -#define BP_SSP_CMD0_BLOCK_COUNT (8) -#define BM_SSP_CMD0_BLOCK_COUNT (0xff << 8) -#define BP_SSP_CMD0_CMD (0) -#define BM_SSP_CMD0_CMD (0xff) -#define HW_SSP_CMD1 0x020 -#define HW_SSP_XFER_SIZE 0x030 -#define HW_SSP_BLOCK_SIZE 0x040 -#define BP_SSP_BLOCK_SIZE_BLOCK_COUNT (4) -#define BM_SSP_BLOCK_SIZE_BLOCK_COUNT (0xffffff << 4) -#define BP_SSP_BLOCK_SIZE_BLOCK_SIZE (0) -#define BM_SSP_BLOCK_SIZE_BLOCK_SIZE (0xf) -#define HW_SSP_TIMING (ssp_is_old() ? 0x050 : 0x070) -#define BP_SSP_TIMING_TIMEOUT (16) -#define BM_SSP_TIMING_TIMEOUT (0xffff << 16) -#define BP_SSP_TIMING_CLOCK_DIVIDE (8) -#define BM_SSP_TIMING_CLOCK_DIVIDE (0xff << 8) -#define BP_SSP_TIMING_CLOCK_RATE (0) -#define BM_SSP_TIMING_CLOCK_RATE (0xff) -#define HW_SSP_CTRL1 (ssp_is_old() ? 0x060 : 0x080) -#define BM_SSP_CTRL1_SDIO_IRQ (1 << 31) -#define BM_SSP_CTRL1_SDIO_IRQ_EN (1 << 30) -#define BM_SSP_CTRL1_RESP_ERR_IRQ (1 << 29) -#define BM_SSP_CTRL1_RESP_ERR_IRQ_EN (1 << 28) -#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ (1 << 27) -#define BM_SSP_CTRL1_RESP_TIMEOUT_IRQ_EN (1 << 26) -#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ (1 << 25) -#define BM_SSP_CTRL1_DATA_TIMEOUT_IRQ_EN (1 << 24) -#define BM_SSP_CTRL1_DATA_CRC_IRQ (1 << 23) -#define BM_SSP_CTRL1_DATA_CRC_IRQ_EN (1 << 22) -#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ (1 << 21) -#define BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ_EN (1 << 20) -#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ (1 << 17) -#define BM_SSP_CTRL1_RECV_TIMEOUT_IRQ_EN (1 << 16) -#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ (1 << 15) -#define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN (1 << 14) -#define BM_SSP_CTRL1_DMA_ENABLE (1 << 13) -#define BM_SSP_CTRL1_POLARITY (1 << 9) -#define BP_SSP_CTRL1_WORD_LENGTH (4) -#define BM_SSP_CTRL1_WORD_LENGTH (0xf << 4) -#define BP_SSP_CTRL1_SSP_MODE (0) -#define BM_SSP_CTRL1_SSP_MODE (0xf) -#define HW_SSP_SDRESP0 (ssp_is_old() ? 0x080 : 0x0a0) -#define HW_SSP_SDRESP1 (ssp_is_old() ? 0x090 : 0x0b0) -#define HW_SSP_SDRESP2 (ssp_is_old() ? 0x0a0 : 0x0c0) -#define HW_SSP_SDRESP3 (ssp_is_old() ? 0x0b0 : 0x0d0) -#define HW_SSP_STATUS (ssp_is_old() ? 0x0c0 : 0x100) -#define BM_SSP_STATUS_CARD_DETECT (1 << 28) -#define BM_SSP_STATUS_SDIO_IRQ (1 << 17) -#define HW_SSP_VERSION (cpu_is_mx23() ? 0x110 : 0x130) -#define BP_SSP_VERSION_MAJOR (24) - -#define BF_SSP(value, field) (((value) << BP_SSP_##field) & BM_SSP_##field) - -#define MXS_MMC_IRQ_BITS (BM_SSP_CTRL1_SDIO_IRQ | \ - BM_SSP_CTRL1_RESP_ERR_IRQ | \ - BM_SSP_CTRL1_RESP_TIMEOUT_IRQ | \ - BM_SSP_CTRL1_DATA_TIMEOUT_IRQ | \ - BM_SSP_CTRL1_DATA_CRC_IRQ | \ - BM_SSP_CTRL1_FIFO_UNDERRUN_IRQ | \ - BM_SSP_CTRL1_RECV_TIMEOUT_IRQ | \ - BM_SSP_CTRL1_FIFO_OVERRUN_IRQ) - -#define SSP_PIO_NUM 3 +#define rev_struct (host->version) struct mxs_mmc_host { struct mmc_host *mmc; -- 1.7.1 ------------------------------------------------------------------------------ For Developers, A Lot Can Happen In A Second. Boundary is the first to Know...and Tell You. Monitor Your Applications in Ultra-Fine Resolution. Try it FREE! http://p.sf.net/sfu/Boundary-d2dvs2 ^ permalink raw reply related [flat|nested] 7+ messages in thread
[parent not found: <1334795434-8780-1-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>]
* [PATCH 2/2] spi: Add initial support for spi-mxs [not found] ` <1334795434-8780-1-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> @ 2012-04-19 0:30 ` Fabio Estevam [not found] ` <1334795434-8780-2-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 2012-04-20 2:59 ` [PATCH 1/2] ARM: mxs: Provide a common header file for SSP controller Shawn Guo 1 sibling, 1 reply; 7+ messages in thread From: Fabio Estevam @ 2012-04-19 0:30 UTC (permalink / raw) To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f Cc: Fabio Estevam, snijsure-4jo+YWezP1RWk0Htik3J/w, marek.vasut-Re5JQEeQqe8AvxtiuMwx3w, kernel-bIcnvbaLZ9MEGnE8C9+IrQ, shawn.guo-QSEj5FYQhm4dnm+yROfE0A Add initial support for the spi driver on mxs processors. Currently only PIO mode is supported. Tested with a sst25vf016b spi flash on a mx28evk board using mtd-utils. Signed-off-by: Fabio Estevam <fabio.estevam-KZfg59tc24xl57MIdRCFDg@public.gmane.org> --- It still does not contain DT support, but I wanted to post it as is, so that people can test it and I would also like to get some initial feedback. arch/arm/mach-mxs/include/mach/ssp-regs.h | 32 ++ drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/spi-mxs.c | 457 +++++++++++++++++++++++++++++ 4 files changed, 496 insertions(+), 0 deletions(-) create mode 100644 drivers/spi/spi-mxs.c diff --git a/arch/arm/mach-mxs/include/mach/ssp-regs.h b/arch/arm/mach-mxs/include/mach/ssp-regs.h index 4bb0b27..fc467fa 100644 --- a/arch/arm/mach-mxs/include/mach/ssp-regs.h +++ b/arch/arm/mach-mxs/include/mach/ssp-regs.h @@ -27,6 +27,10 @@ /* SSP registers */ #define HW_SSP_CTRL0 0x000 +#define HW_SSP_CTRL0_SET 0x00000004 +#define HW_SSP_CTRL0_CLR 0x00000008 +#define HW_SSP_CTRL0_TOG 0x0000000c +#define BM_SSP_CTRL0_LOCK_CS 0x08000000 #define BM_SSP_CTRL0_RUN (1 << 29) #define BM_SSP_CTRL0_SDIO_IRQ_CHECK (1 << 28) #define BM_SSP_CTRL0_IGNORE_CRC (1 << 26) @@ -41,6 +45,10 @@ #define BP_SSP_CTRL0_XFER_COUNT 0 #define BM_SSP_CTRL0_XFER_COUNT 0xffff #define HW_SSP_CMD0 0x010 +#define HW_SSP_CMD0_SET 0x014 +#define HW_SSP_CMD0_CLR 0x018 +#define HW_SSP_CMD0_TOG 0x01c + #define BM_SSP_CMD0_DBL_DATA_RATE_EN (1 << 25) #define BM_SSP_CMD0_SLOW_CLKING_EN (1 << 22) #define BM_SSP_CMD0_CONT_CLKING_EN (1 << 21) @@ -63,8 +71,12 @@ #define BM_SSP_TIMING_TIMEOUT (0xffff << 16) #define BP_SSP_TIMING_CLOCK_DIVIDE 8 #define BM_SSP_TIMING_CLOCK_DIVIDE (0xff << 8) +#define BF_SSP_TIMING_CLOCK_DIVIDE(v) \ + (((v) << 8) & BM_SSP_TIMING_CLOCK_DIVIDE) #define BP_SSP_TIMING_CLOCK_RATE 0 #define BM_SSP_TIMING_CLOCK_RATE 0xff +#define BF_SSP_TIMING_CLOCK_RATE(v) \ + (((v) << 0) & BM_SSP_TIMING_CLOCK_RATE) #define HW_SSP_CTRL1 (ssp_is_old() ? 0x060 : 0x080) #define BM_SSP_CTRL1_SDIO_IRQ (1 << 31) #define BM_SSP_CTRL1_SDIO_IRQ_EN (1 << 30) @@ -83,11 +95,30 @@ #define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ (1 << 15) #define BM_SSP_CTRL1_FIFO_OVERRUN_IRQ_EN (1 << 14) #define BM_SSP_CTRL1_DMA_ENABLE (1 << 13) +#define BM_SSP_CTRL1_PHASE 0x00000400 #define BM_SSP_CTRL1_POLARITY (1 << 9) #define BP_SSP_CTRL1_WORD_LENGTH 4 #define BM_SSP_CTRL1_WORD_LENGTH (0xf << 4) +#define BF_SSP_CTRL1_WORD_LENGTH(v) \ + (((v) << 4) & BM_SSP_CTRL1_WORD_LENGTH) +#define BV_SSP_CTRL1_WORD_LENGTH__RESERVED0 0x0 +#define BV_SSP_CTRL1_WORD_LENGTH__RESERVED1 0x1 +#define BV_SSP_CTRL1_WORD_LENGTH__RESERVED2 0x2 +#define BV_SSP_CTRL1_WORD_LENGTH__FOUR_BITS 0x3 +#define BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS 0x7 +#define BV_SSP_CTRL1_WORD_LENGTH__SIXTEEN_BITS 0xF +#define BM_SSP_CTRL0_WAIT_FOR_CMD 0x00100000 #define BP_SSP_CTRL1_SSP_MODE 0 #define BM_SSP_CTRL1_SSP_MODE 0xf +#define BF_SSP_CTRL1_SSP_MODE(v) \ + (((v) << 0) & BM_SSP_CTRL1_SSP_MODE) +#define BV_SSP_CTRL1_SSP_MODE__SPI 0x0 +#define BV_SSP_CTRL1_SSP_MODE__SSI 0x1 +#define BV_SSP_CTRL1_SSP_MODE__SD_MMC 0x3 +#define BV_SSP_CTRL1_SSP_MODE__MS 0x4 + +#define HW_SSP_DATA 0x090 + #define HW_SSP_SDRESP0 (ssp_is_old() ? 0x080 : 0x0a0) #define HW_SSP_SDRESP1 (ssp_is_old() ? 0x090 : 0x0b0) #define HW_SSP_SDRESP2 (ssp_is_old() ? 0x0a0 : 0x0c0) @@ -95,6 +126,7 @@ #define HW_SSP_STATUS (ssp_is_old() ? 0x0c0 : 0x100) #define BM_SSP_STATUS_CARD_DETECT (1 << 28) #define BM_SSP_STATUS_SDIO_IRQ (1 << 17) +#define BM_SSP_STATUS_FIFO_EMPTY 0x00000020 #define HW_SSP_VERSION (cpu_is_mx23() ? 0x110 : 0x130) #define BP_SSP_VERSION_MAJOR 24 diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 3ed7483..f951604 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -199,6 +199,12 @@ config SPI_MPC512x_PSC This enables using the Freescale MPC5121 Programmable Serial Controller in SPI master mode. +config SPI_MXS + tristate "Freescale MXS SPI controller" + depends on ARCH_MXS + help + SPI driver for Freescale MXS devices + config SPI_FSL_LIB tristate depends on FSL_SOC diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index a1d48e0..0e6fe03 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o +obj-$(CONFIG_SPI_MXS) += spi-mxs.o obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c new file mode 100644 index 0000000..3550ab6 --- /dev/null +++ b/drivers/spi/spi-mxs.c @@ -0,0 +1,457 @@ +/* + * Freescale MXS SPI master driver + * + * Heavily based on spi-stmp.c, which is: + * Author: dmitry pervushin <dimka-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org> + * + * Copyright 2012 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 <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <mach/mxs.h> +#include <mach/ssp-regs.h> +#include <mach/common.h> + +#define SSP_TIMEOUT 200 /* 200 ms */ + +#define rev_struct (ss->version) + +struct mxs_spi { + void __iomem *regs; /* vaddr of the control registers */ + + u32 speed_khz; + u32 divider; + + struct clk *clk; + struct device *master_dev; + + struct work_struct work; + struct workqueue_struct *workqueue; + spinlock_t lock; + struct list_head queue; + + u32 version; +}; + +static int mxs_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + u8 bits_per_word; + u32 hz; + struct mxs_spi *ss; + u16 rate; + + ss = spi_master_get_devdata(spi->master); + + bits_per_word = spi->bits_per_word; + if (t && t->bits_per_word) + bits_per_word = t->bits_per_word; +/* + * Calculate speed: + * - by default, use maximum speed from ssp clk + * - if device overrides it, use it + * - if transfer specifies other speed, use transfer's one + */ + hz = 1000 * ss->speed_khz / ss->divider; + if (spi->max_speed_hz) + hz = min(hz, spi->max_speed_hz); + if (t && t->speed_hz) + hz = min(hz, t->speed_hz); + + if (hz == 0) { + dev_err(&spi->dev, "Cannot continue with zero clock\n"); + return -EINVAL; + } + + if (bits_per_word != 8) { + dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", + __func__, bits_per_word); + return -EINVAL; + } + + dev_dbg(&spi->dev, "Requested clk rate = %uHz, max = %ukHz/%d = %uHz\n", + hz, ss->speed_khz, ss->divider, + ss->speed_khz * 1000 / ss->divider); + + if (ss->speed_khz * 1000 / ss->divider < hz) { + dev_err(&spi->dev, "%s, unsupported clock rate %uHz\n", + __func__, hz); + return -EINVAL; + } + + rate = 1000 * ss->speed_khz / ss->divider / hz; + + __raw_writel(BF_SSP_TIMING_CLOCK_DIVIDE(ss->divider) | + BF_SSP_TIMING_CLOCK_RATE(rate - 1), + ss->regs + HW_SSP_TIMING); + + __raw_writel(BF_SSP_CTRL1_SSP_MODE(BV_SSP_CTRL1_SSP_MODE__SPI) | + BF_SSP_CTRL1_WORD_LENGTH + (BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) | + ((spi->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) | + ((spi->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0), + ss->regs + HW_SSP_CTRL1); + + __raw_writel(0x0, ss->regs + HW_SSP_CMD0_SET); + + return 0; +} + +static void mxs_spi_cleanup(struct spi_device *spi) +{ + return; +} + +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_CPOL | SPI_CPHA) +static int mxs_spi_setup(struct spi_device *spi) +{ + struct mxs_spi *ss; + int err = 0; + + ss = spi_master_get_devdata(spi->master); + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + if (spi->mode & ~MODEBITS) + return -EINVAL; + + err = mxs_spi_setup_transfer(spi, NULL); + if (err) + dev_err(&spi->dev, "Failed to setup transfer: %d\n", err); + + return err; +} + +static inline u32 mxs_spi_cs(unsigned cs) +{ + return ((cs & 1) ? BM_SSP_CTRL0_WAIT_FOR_CMD : 0) | + ((cs & 2) ? BM_SSP_CTRL0_WAIT_FOR_IRQ : 0); +} + +static inline void mxs_spi_enable(struct mxs_spi *ss) +{ + __raw_writel(BM_SSP_CTRL0_LOCK_CS, ss->regs + HW_SSP_CTRL0_SET); + __raw_writel(BM_SSP_CTRL0_IGNORE_CRC, ss->regs + HW_SSP_CTRL0_CLR); +} + +static inline void mxs_spi_disable(struct mxs_spi *ss) +{ + __raw_writel(BM_SSP_CTRL0_LOCK_CS, ss->regs + HW_SSP_CTRL0_CLR); + __raw_writel(BM_SSP_CTRL0_IGNORE_CRC, ss->regs + HW_SSP_CTRL0_SET); +} + +int mxs_ssp_wait_set(struct mxs_spi *ss, int offset, int mask) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); + + while (!(readl_relaxed(&ss->regs + offset) & mask)) { + udelay(1); + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + } + return 0; +} + +int mxs_ssp_wait_clr(struct mxs_spi *ss, int offset, int mask) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); + + while ((readl_relaxed(&ss->regs + offset) & mask)) { + udelay(1); + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + } + return 0; +} + +static int mxs_spi_txrx_pio(struct mxs_spi *ss, int cs, + unsigned char *buf, int len, + int *first, int *last, int write) +{ + if (*first) { + mxs_spi_enable(ss); + *first = 0; + } + + __raw_writel(mxs_spi_cs(cs), ss->regs + HW_SSP_CTRL0_SET); + + while (len--) { + if (*last && len == 0) { + mxs_spi_disable(ss); + *last = 0; + } + + if (ss->version > 3) { + __raw_writel(1, ss->regs + HW_SSP_XFER_SIZE); + } else { + __raw_writel(BM_SSP_CTRL0_XFER_COUNT, + ss->regs + HW_SSP_CTRL0_CLR); + __raw_writel(1, ss->regs + HW_SSP_CTRL0_SET); + } + + if (write) + __raw_writel(BM_SSP_CTRL0_READ, + ss->regs + HW_SSP_CTRL0_CLR); + else + __raw_writel(BM_SSP_CTRL0_READ, + ss->regs + HW_SSP_CTRL0_SET); + + /* Activate Run bit */ + __raw_writel(BM_SSP_CTRL0_RUN, ss->regs + HW_SSP_CTRL0_SET); + + + if (mxs_ssp_wait_set(ss->regs, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN)) + return -ETIMEDOUT; + + if (write) + __raw_writel(*buf, ss->regs + HW_SSP_DATA); + + /* Set TRANSFER */ + __raw_writel(BM_SSP_CTRL0_DATA_XFER, + ss->regs + HW_SSP_CTRL0_SET); + + if (!write) { + if (mxs_ssp_wait_clr(ss->regs, HW_SSP_STATUS, + BM_SSP_STATUS_FIFO_EMPTY)) + return -ETIMEDOUT; + + *buf = (__raw_readl(ss->regs + HW_SSP_DATA) & 0xFF); + } + + if (mxs_ssp_wait_clr(ss->regs, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN)) + return -ETIMEDOUT; + /* advance to the next byte */ + buf++; + } + return len < 0 ? 0 : -ETIMEDOUT; +} + +static int mxs_spi_handle_message(struct mxs_spi *ss, struct spi_message *m) +{ + int first, last; + struct spi_transfer *t, *tmp_t; + int status = 0; + int cs; + + first = last = 0; + + cs = m->spi->chip_select; + + list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) { + + mxs_spi_setup_transfer(m->spi, t); + + if (&t->transfer_list == m->transfers.next) + first = !0; + if (&t->transfer_list == m->transfers.prev) + last = !0; + if (t->rx_buf && t->tx_buf) { + pr_debug("%s: cannot send and receive simultaneously\n", + __func__); + return -EINVAL; + } + /* + * REVISIT: + * here driver completely ignores setting of t->cs_change + */ + if (t->tx_buf) + status = mxs_spi_txrx_pio(ss, cs, (void *)t->tx_buf, + t->len, &first, &last, 1); + if (t->rx_buf) + status = mxs_spi_txrx_pio(ss, cs, t->rx_buf, + t->len, &first, &last, 0); + m->actual_length += t->len; + if (status) + break; + + first = last = 0; + } + return status; +} + +/* + * mxs_spi_handle + * + * The workhorse of the driver - it handles messages from the list + */ +static void mxs_spi_handle(struct work_struct *w) +{ + struct mxs_spi *ss = container_of(w, struct mxs_spi, work); + unsigned long flags; + struct spi_message *m; + + BUG_ON(w == NULL); + + spin_lock_irqsave(&ss->lock, flags); + while (!list_empty(&ss->queue)) { + m = list_entry(ss->queue.next, struct spi_message, queue); + list_del_init(&m->queue); + spin_unlock_irqrestore(&ss->lock, flags); + + m->status = mxs_spi_handle_message(ss, m); + if (m->complete) + m->complete(m->context); + + spin_lock_irqsave(&ss->lock, flags); + } + spin_unlock_irqrestore(&ss->lock, flags); + + return; +} + +/* + * mxs_spi_transfer + * + * Called indirectly from spi_async, queues all the messages to + * spi_handle_message + * + */ +static int mxs_spi_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct mxs_spi *ss = spi_master_get_devdata(spi->master); + unsigned long flags; + + m->actual_length = 0; + m->status = -EINPROGRESS; + spin_lock_irqsave(&ss->lock, flags); + list_add_tail(&m->queue, &ss->queue); + queue_work(ss->workqueue, &ss->work); + spin_unlock_irqrestore(&ss->lock, flags); + + return 0; +} + +static int __devinit mxs_spi_probe(struct platform_device *dev) +{ + int ret; + struct spi_master *master; + struct mxs_spi *ss; + struct resource *res; + + master = spi_alloc_master(&dev->dev, sizeof(struct mxs_spi)); + ss = spi_master_get_devdata(master); + ss->master_dev = &dev->dev; + + if (!master) + return -ENOMEM; + + platform_set_drvdata(dev, master); + + INIT_WORK(&ss->work, mxs_spi_handle); + INIT_LIST_HEAD(&ss->queue); + spin_lock_init(&ss->lock); + ss->workqueue = create_singlethread_workqueue(dev_name(&dev->dev)); + master->transfer = mxs_spi_transfer; + master->setup = mxs_spi_setup; + master->cleanup = mxs_spi_cleanup; + master->mode_bits = MODEBITS; + + master->bus_num = dev->id; + master->num_chipselect = 1; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + ss->regs = devm_request_and_ioremap(&dev->dev, res); + if (!ss->regs) + return -EBUSY; + + mxs_reset_block(ss->regs); + ss->clk = clk_get(&dev->dev, NULL); + if (IS_ERR(ss->clk)) { + ret = PTR_ERR(ss->clk); + dev_err(&dev->dev, "cannot get spi clk\n"); + goto out_put_master; + } + + clk_prepare_enable(ss->clk); + + ss->speed_khz = clk_get_rate(ss->clk) / 1000; + ss->divider = 2; + dev_dbg(&dev->dev, "Max possible speed %d = %ld/%d kHz\n", + ss->speed_khz, clk_get_rate(ss->clk), ss->divider); + + ss->version = __raw_readl(ss->regs + HW_SSP_VERSION) >> 24; + + ret = spi_register_master(master); + if (ret) { + dev_err(&dev->dev, "cannot register spi master, %d\n", ret); + goto out_clk_put; + } + + dev_info(&dev->dev, "driver probed\n"); + + return 0; + +out_clk_put: + clk_disable_unprepare(ss->clk); + clk_put(ss->clk); +out_put_master: + spi_master_put(master); + if (ss->workqueue) + destroy_workqueue(ss->workqueue); + platform_set_drvdata(dev, NULL); + + return ret; +} + +static int __devexit mxs_spi_remove(struct platform_device *dev) +{ + struct mxs_spi *ss; + struct spi_master *master; + + master = platform_get_drvdata(dev); + if (!master) + goto out0; + ss = spi_master_get_devdata(master); + + clk_disable_unprepare(ss->clk); + clk_put(ss->clk); + spi_unregister_master(master); + + destroy_workqueue(ss->workqueue); + spi_master_put(master); + platform_set_drvdata(dev, NULL); +out0: + return 0; +} + +static struct platform_driver mxs_spi_driver = { + .probe = mxs_spi_probe, + .remove = __devexit_p(mxs_spi_remove), + .driver = { + .name = "mxs-spi", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(mxs_spi_driver); + +MODULE_AUTHOR("dmitry pervushin <dimka-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>"); +MODULE_AUTHOR("Fabio Estevam <fabio.estevam-KZfg59tc24xl57MIdRCFDg@public.gmane.org"); +MODULE_DESCRIPTION("MXS SPI master driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mxs-spi"); -- 1.7.1 ------------------------------------------------------------------------------ For Developers, A Lot Can Happen In A Second. Boundary is the first to Know...and Tell You. Monitor Your Applications in Ultra-Fine Resolution. Try it FREE! http://p.sf.net/sfu/Boundary-d2dvs2 ^ permalink raw reply related [flat|nested] 7+ messages in thread
[parent not found: <1334795434-8780-2-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>]
* Re: [PATCH 2/2] spi: Add initial support for spi-mxs [not found] ` <1334795434-8780-2-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> @ 2012-04-20 4:12 ` Shawn Guo 0 siblings, 0 replies; 7+ messages in thread From: Shawn Guo @ 2012-04-20 4:12 UTC (permalink / raw) To: Fabio Estevam Cc: Fabio Estevam, snijsure-4jo+YWezP1RWk0Htik3J/w, marek.vasut-Re5JQEeQqe8AvxtiuMwx3w, kernel-bIcnvbaLZ9MEGnE8C9+IrQ, spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f On Wed, Apr 18, 2012 at 09:30:34PM -0300, Fabio Estevam wrote: ... > #define HW_SSP_VERSION (cpu_is_mx23() ? 0x110 : 0x130) This is something needs a bit work (as well as mxs-mmc). The VERSION register sits on different address between imx23 and imx28, so it loses its point. The cpu_is_xxx stuff does not scale for long term and will not cope with device tree support. We really need to get rid of it. > +/* > + * Freescale MXS SPI master driver > + * > + * Heavily based on spi-stmp.c, which is: > + * Author: dmitry pervushin <dimka-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org> > + * > + * Copyright 2012 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 <linux/module.h> > +#include <linux/init.h> > +#include <linux/interrupt.h> > +#include <linux/platform_device.h> > +#include <linux/spi/spi.h> > +#include <linux/err.h> > +#include <linux/clk.h> > +#include <linux/io.h> > +#include <linux/dma-mapping.h> What's this for? > +#include <linux/errno.h> > +#include <linux/delay.h> > +#include <mach/mxs.h> When you get rid of the using of cpu_is_xxx, the inclusion can be removed. > +#include <mach/ssp-regs.h> > +#include <mach/common.h> With Wolfram's stmp-style devices support merged, you can save this inclusion. > + > +#define SSP_TIMEOUT 200 /* 200 ms */ > + > +#define rev_struct (ss->version) > + What's this for? > +struct mxs_spi { > + void __iomem *regs; /* vaddr of the control registers */ > + > + u32 speed_khz; > + u32 divider; > + > + struct clk *clk; > + struct device *master_dev; > + > + struct work_struct work; > + struct workqueue_struct *workqueue; > + spinlock_t lock; > + struct list_head queue; > + > + u32 version; > +}; > + > +static int mxs_spi_setup_transfer(struct spi_device *spi, > + struct spi_transfer *t) > +{ > + u8 bits_per_word; > + u32 hz; > + struct mxs_spi *ss; > + u16 rate; > + > + ss = spi_master_get_devdata(spi->master); > + > + bits_per_word = spi->bits_per_word; > + if (t && t->bits_per_word) > + bits_per_word = t->bits_per_word; > +/* > + * Calculate speed: > + * - by default, use maximum speed from ssp clk > + * - if device overrides it, use it > + * - if transfer specifies other speed, use transfer's one > + */ Indent is missed. > + hz = 1000 * ss->speed_khz / ss->divider; > + if (spi->max_speed_hz) > + hz = min(hz, spi->max_speed_hz); > + if (t && t->speed_hz) > + hz = min(hz, t->speed_hz); > + > + if (hz == 0) { > + dev_err(&spi->dev, "Cannot continue with zero clock\n"); > + return -EINVAL; > + } > + > + if (bits_per_word != 8) { > + dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", > + __func__, bits_per_word); > + return -EINVAL; > + } > + > + dev_dbg(&spi->dev, "Requested clk rate = %uHz, max = %ukHz/%d = %uHz\n", > + hz, ss->speed_khz, ss->divider, > + ss->speed_khz * 1000 / ss->divider); > + > + if (ss->speed_khz * 1000 / ss->divider < hz) { > + dev_err(&spi->dev, "%s, unsupported clock rate %uHz\n", > + __func__, hz); > + return -EINVAL; > + } > + > + rate = 1000 * ss->speed_khz / ss->divider / hz; > + > + __raw_writel(BF_SSP_TIMING_CLOCK_DIVIDE(ss->divider) | > + BF_SSP_TIMING_CLOCK_RATE(rate - 1), > + ss->regs + HW_SSP_TIMING); > + writel or writel_relaxed? > + __raw_writel(BF_SSP_CTRL1_SSP_MODE(BV_SSP_CTRL1_SSP_MODE__SPI) | > + BF_SSP_CTRL1_WORD_LENGTH > + (BV_SSP_CTRL1_WORD_LENGTH__EIGHT_BITS) | > + ((spi->mode & SPI_CPOL) ? BM_SSP_CTRL1_POLARITY : 0) | > + ((spi->mode & SPI_CPHA) ? BM_SSP_CTRL1_PHASE : 0), > + ss->regs + HW_SSP_CTRL1); > + > + __raw_writel(0x0, ss->regs + HW_SSP_CMD0_SET); > + > + return 0; > +} > + > +static void mxs_spi_cleanup(struct spi_device *spi) > +{ > + return; > +} > + > +/* the spi->mode bits understood by this driver: */ > +#define MODEBITS (SPI_CPOL | SPI_CPHA) > +static int mxs_spi_setup(struct spi_device *spi) > +{ > + struct mxs_spi *ss; > + int err = 0; > + > + ss = spi_master_get_devdata(spi->master); > + > + if (!spi->bits_per_word) > + spi->bits_per_word = 8; > + > + if (spi->mode & ~MODEBITS) > + return -EINVAL; > + > + err = mxs_spi_setup_transfer(spi, NULL); > + if (err) > + dev_err(&spi->dev, "Failed to setup transfer: %d\n", err); > + > + return err; > +} > + > +static inline u32 mxs_spi_cs(unsigned cs) > +{ > + return ((cs & 1) ? BM_SSP_CTRL0_WAIT_FOR_CMD : 0) | > + ((cs & 2) ? BM_SSP_CTRL0_WAIT_FOR_IRQ : 0); > +} > + > +static inline void mxs_spi_enable(struct mxs_spi *ss) > +{ > + __raw_writel(BM_SSP_CTRL0_LOCK_CS, ss->regs + HW_SSP_CTRL0_SET); > + __raw_writel(BM_SSP_CTRL0_IGNORE_CRC, ss->regs + HW_SSP_CTRL0_CLR); > +} > + > +static inline void mxs_spi_disable(struct mxs_spi *ss) > +{ > + __raw_writel(BM_SSP_CTRL0_LOCK_CS, ss->regs + HW_SSP_CTRL0_CLR); > + __raw_writel(BM_SSP_CTRL0_IGNORE_CRC, ss->regs + HW_SSP_CTRL0_SET); > +} > + > +int mxs_ssp_wait_set(struct mxs_spi *ss, int offset, int mask) static? > +{ > + unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); > + > + while (!(readl_relaxed(&ss->regs + offset) & mask)) { > + udelay(1); > + if (time_after(jiffies, timeout)) > + return -ETIMEDOUT; > + } > + return 0; > +} > + > +int mxs_ssp_wait_clr(struct mxs_spi *ss, int offset, int mask) ditto > +{ > + unsigned long timeout = jiffies + msecs_to_jiffies(SSP_TIMEOUT); > + > + while ((readl_relaxed(&ss->regs + offset) & mask)) { > + udelay(1); > + if (time_after(jiffies, timeout)) > + return -ETIMEDOUT; > + } > + return 0; > +} > + > +static int mxs_spi_txrx_pio(struct mxs_spi *ss, int cs, > + unsigned char *buf, int len, > + int *first, int *last, int write) > +{ > + if (*first) { > + mxs_spi_enable(ss); > + *first = 0; > + } > + > + __raw_writel(mxs_spi_cs(cs), ss->regs + HW_SSP_CTRL0_SET); > + > + while (len--) { > + if (*last && len == 0) { > + mxs_spi_disable(ss); > + *last = 0; > + } > + > + if (ss->version > 3) { > + __raw_writel(1, ss->regs + HW_SSP_XFER_SIZE); > + } else { > + __raw_writel(BM_SSP_CTRL0_XFER_COUNT, > + ss->regs + HW_SSP_CTRL0_CLR); > + __raw_writel(1, ss->regs + HW_SSP_CTRL0_SET); > + } > + > + if (write) > + __raw_writel(BM_SSP_CTRL0_READ, > + ss->regs + HW_SSP_CTRL0_CLR); > + else > + __raw_writel(BM_SSP_CTRL0_READ, > + ss->regs + HW_SSP_CTRL0_SET); > + > + /* Activate Run bit */ > + __raw_writel(BM_SSP_CTRL0_RUN, ss->regs + HW_SSP_CTRL0_SET); > + > + > + if (mxs_ssp_wait_set(ss->regs, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN)) > + return -ETIMEDOUT; It should return what mxs_ssp_wait_set() returns. > + > + if (write) > + __raw_writel(*buf, ss->regs + HW_SSP_DATA); > + > + /* Set TRANSFER */ > + __raw_writel(BM_SSP_CTRL0_DATA_XFER, > + ss->regs + HW_SSP_CTRL0_SET); > + > + if (!write) { > + if (mxs_ssp_wait_clr(ss->regs, HW_SSP_STATUS, > + BM_SSP_STATUS_FIFO_EMPTY)) > + return -ETIMEDOUT; > + > + *buf = (__raw_readl(ss->regs + HW_SSP_DATA) & 0xFF); > + } > + > + if (mxs_ssp_wait_clr(ss->regs, HW_SSP_CTRL0, BM_SSP_CTRL0_RUN)) > + return -ETIMEDOUT; > + /* advance to the next byte */ > + buf++; > + } > + return len < 0 ? 0 : -ETIMEDOUT; > +} > + > +static int mxs_spi_handle_message(struct mxs_spi *ss, struct spi_message *m) > +{ > + int first, last; > + struct spi_transfer *t, *tmp_t; > + int status = 0; > + int cs; > + > + first = last = 0; > + > + cs = m->spi->chip_select; > + > + list_for_each_entry_safe(t, tmp_t, &m->transfers, transfer_list) { > + > + mxs_spi_setup_transfer(m->spi, t); > + > + if (&t->transfer_list == m->transfers.next) > + first = !0; Use bool for first and last? > + if (&t->transfer_list == m->transfers.prev) > + last = !0; > + if (t->rx_buf && t->tx_buf) { > + pr_debug("%s: cannot send and receive simultaneously\n", > + __func__); Manage to use dev_dbg? > + return -EINVAL; > + } > + /* > + * REVISIT: > + * here driver completely ignores setting of t->cs_change > + */ > + if (t->tx_buf) > + status = mxs_spi_txrx_pio(ss, cs, (void *)t->tx_buf, Why the cast? > + t->len, &first, &last, 1); > + if (t->rx_buf) > + status = mxs_spi_txrx_pio(ss, cs, t->rx_buf, > + t->len, &first, &last, 0); > + m->actual_length += t->len; > + if (status) > + break; > + > + first = last = 0; > + } > + return status; > +} > + > +/* > + * mxs_spi_handle > + * > + * The workhorse of the driver - it handles messages from the list > + */ > +static void mxs_spi_handle(struct work_struct *w) > +{ > + struct mxs_spi *ss = container_of(w, struct mxs_spi, work); > + unsigned long flags; > + struct spi_message *m; > + > + BUG_ON(w == NULL); > + > + spin_lock_irqsave(&ss->lock, flags); > + while (!list_empty(&ss->queue)) { > + m = list_entry(ss->queue.next, struct spi_message, queue); > + list_del_init(&m->queue); > + spin_unlock_irqrestore(&ss->lock, flags); > + > + m->status = mxs_spi_handle_message(ss, m); > + if (m->complete) > + m->complete(m->context); > + > + spin_lock_irqsave(&ss->lock, flags); > + } > + spin_unlock_irqrestore(&ss->lock, flags); > + > + return; > +} > + > +/* > + * mxs_spi_transfer > + * > + * Called indirectly from spi_async, queues all the messages to > + * spi_handle_message > + * > + */ > +static int mxs_spi_transfer(struct spi_device *spi, struct spi_message *m) > +{ > + struct mxs_spi *ss = spi_master_get_devdata(spi->master); > + unsigned long flags; > + > + m->actual_length = 0; > + m->status = -EINPROGRESS; > + spin_lock_irqsave(&ss->lock, flags); > + list_add_tail(&m->queue, &ss->queue); > + queue_work(ss->workqueue, &ss->work); > + spin_unlock_irqrestore(&ss->lock, flags); > + > + return 0; > +} > + > +static int __devinit mxs_spi_probe(struct platform_device *dev) We usually name it pdev. > +{ > + int ret; > + struct spi_master *master; > + struct mxs_spi *ss; > + struct resource *res; > + > + master = spi_alloc_master(&dev->dev, sizeof(struct mxs_spi)); sizeof(*ss) > + ss = spi_master_get_devdata(master); > + ss->master_dev = &dev->dev; > + > + if (!master) > + return -ENOMEM; > + Shouldn't the check be put right after spi_alloc_master call? > + platform_set_drvdata(dev, master); > + > + INIT_WORK(&ss->work, mxs_spi_handle); > + INIT_LIST_HEAD(&ss->queue); > + spin_lock_init(&ss->lock); > + ss->workqueue = create_singlethread_workqueue(dev_name(&dev->dev)); ... > + master->transfer = mxs_spi_transfer; > + master->setup = mxs_spi_setup; > + master->cleanup = mxs_spi_cleanup; > + master->mode_bits = MODEBITS; > + It makes more sense to move this new line to above. > + master->bus_num = dev->id; > + master->num_chipselect = 1; > + > + res = platform_get_resource(dev, IORESOURCE_MEM, 0); > + if (!res) > + return -ENOENT; > + We do not have to check that. We can pass whatever we get from platform_get_resource into devm_request_and_ioremap, and the latter will do sanity check. > + ss->regs = devm_request_and_ioremap(&dev->dev, res); > + if (!ss->regs) > + return -EBUSY; The kernel doc of devm_request_and_ioremap suggests -EADDRNOTAVAIL. > + > + mxs_reset_block(ss->regs); > + ss->clk = clk_get(&dev->dev, NULL); > + if (IS_ERR(ss->clk)) { > + ret = PTR_ERR(ss->clk); > + dev_err(&dev->dev, "cannot get spi clk\n"); Put ret into the message? > + goto out_put_master; > + } > + > + clk_prepare_enable(ss->clk); > + > + ss->speed_khz = clk_get_rate(ss->clk) / 1000; > + ss->divider = 2; > + dev_dbg(&dev->dev, "Max possible speed %d = %ld/%d kHz\n", > + ss->speed_khz, clk_get_rate(ss->clk), ss->divider); > + > + ss->version = __raw_readl(ss->regs + HW_SSP_VERSION) >> 24; > + > + ret = spi_register_master(master); > + if (ret) { > + dev_err(&dev->dev, "cannot register spi master, %d\n", ret); > + goto out_clk_put; > + } > + > + dev_info(&dev->dev, "driver probed\n"); > + > + return 0; > + > +out_clk_put: > + clk_disable_unprepare(ss->clk); > + clk_put(ss->clk); > +out_put_master: > + spi_master_put(master); > + if (ss->workqueue) > + destroy_workqueue(ss->workqueue); > + platform_set_drvdata(dev, NULL); I'm wondering if this is really needed. > + > + return ret; > +} > + > +static int __devexit mxs_spi_remove(struct platform_device *dev) > +{ > + struct mxs_spi *ss; > + struct spi_master *master; > + > + master = platform_get_drvdata(dev); > + if (!master) > + goto out0; Will we ever run into this error? > + ss = spi_master_get_devdata(master); > + > + clk_disable_unprepare(ss->clk); > + clk_put(ss->clk); > + spi_unregister_master(master); > + > + destroy_workqueue(ss->workqueue); > + spi_master_put(master); > + platform_set_drvdata(dev, NULL); > +out0: > + return 0; > +} > + > +static struct platform_driver mxs_spi_driver = { > + .probe = mxs_spi_probe, > + .remove = __devexit_p(mxs_spi_remove), > + .driver = { > + .name = "mxs-spi", > + .owner = THIS_MODULE, > + }, Strange indent. Regards, Shawn > +}; > +module_platform_driver(mxs_spi_driver); > + > +MODULE_AUTHOR("dmitry pervushin <dimka-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>"); > +MODULE_AUTHOR("Fabio Estevam <fabio.estevam-KZfg59tc24xl57MIdRCFDg@public.gmane.org"); > +MODULE_DESCRIPTION("MXS SPI master driver"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:mxs-spi"); > -- > 1.7.1 > ------------------------------------------------------------------------------ For Developers, A Lot Can Happen In A Second. Boundary is the first to Know...and Tell You. Monitor Your Applications in Ultra-Fine Resolution. Try it FREE! http://p.sf.net/sfu/Boundary-d2dvs2 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] ARM: mxs: Provide a common header file for SSP controller [not found] ` <1334795434-8780-1-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 2012-04-19 0:30 ` [PATCH 2/2] spi: Add initial support for spi-mxs Fabio Estevam @ 2012-04-20 2:59 ` Shawn Guo [not found] ` <20120420025918.GI22219-rvtDTF3kK1ictlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org> 1 sibling, 1 reply; 7+ messages in thread From: Shawn Guo @ 2012-04-20 2:59 UTC (permalink / raw) To: Fabio Estevam Cc: Fabio Estevam, snijsure-4jo+YWezP1RWk0Htik3J/w, marek.vasut-Re5JQEeQqe8AvxtiuMwx3w, kernel-bIcnvbaLZ9MEGnE8C9+IrQ, spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f, Chris Ball On Wed, Apr 18, 2012 at 09:30:33PM -0300, Fabio Estevam wrote: > On mxs SoCs the SSP controller can act as MMC or SPI controller. > > Remove the SSP related definitions from the mxs-mmc driver and put it on a > common header file. > > This will facilitate the introduction of the spi-mxs driver. > We are trying to remove <mach/*.h> from drivers. And this patch moves to the opposite direction. Regards, Shawn > Cc: Chris Ball <cjb-2X9k7bc8m7Mdnm+yROfE0A@public.gmane.org> > Signed-off-by: Fabio Estevam <fabio.estevam-KZfg59tc24xl57MIdRCFDg@public.gmane.org> > --- > arch/arm/mach-mxs/include/mach/ssp-regs.h | 114 +++++++++++++++++++++++++++++ > drivers/mmc/host/mxs-mmc.c | 93 +----------------------- > 2 files changed, 116 insertions(+), 91 deletions(-) > create mode 100644 arch/arm/mach-mxs/include/mach/ssp-regs.h ------------------------------------------------------------------------------ For Developers, A Lot Can Happen In A Second. Boundary is the first to Know...and Tell You. Monitor Your Applications in Ultra-Fine Resolution. Try it FREE! http://p.sf.net/sfu/Boundary-d2dvs2 ^ permalink raw reply [flat|nested] 7+ messages in thread
[parent not found: <20120420025918.GI22219-rvtDTF3kK1ictlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>]
* Re: [PATCH 1/2] ARM: mxs: Provide a common header file for SSP controller [not found] ` <20120420025918.GI22219-rvtDTF3kK1ictlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org> @ 2012-04-20 3:07 ` Fabio Estevam [not found] ` <CAOMZO5AOmAA93LLQnzHrOf+prH5rmNw7C-ZSpYzpKGUrZ74G1g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> 0 siblings, 1 reply; 7+ messages in thread From: Fabio Estevam @ 2012-04-20 3:07 UTC (permalink / raw) To: Shawn Guo Cc: Fabio Estevam, snijsure-4jo+YWezP1RWk0Htik3J/w, marek.vasut-Re5JQEeQqe8AvxtiuMwx3w, kernel-bIcnvbaLZ9MEGnE8C9+IrQ, spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f, Chris Ball On Thu, Apr 19, 2012 at 11:59 PM, Shawn Guo <shawn.guo@linaro.org> wrote: > We are trying to remove <mach/*.h> from drivers. And this patch moves > to the opposite direction. Where would be a good location for ssp-regs.h then? Regards, Fabio Estevam ------------------------------------------------------------------------------ For Developers, A Lot Can Happen In A Second. Boundary is the first to Know...and Tell You. Monitor Your Applications in Ultra-Fine Resolution. Try it FREE! http://p.sf.net/sfu/Boundary-d2dvs2 _______________________________________________ spi-devel-general mailing list spi-devel-general@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/spi-devel-general ^ permalink raw reply [flat|nested] 7+ messages in thread
[parent not found: <CAOMZO5AOmAA93LLQnzHrOf+prH5rmNw7C-ZSpYzpKGUrZ74G1g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>]
* Re: [PATCH 1/2] ARM: mxs: Provide a common header file for SSP controller [not found] ` <CAOMZO5AOmAA93LLQnzHrOf+prH5rmNw7C-ZSpYzpKGUrZ74G1g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> @ 2012-04-20 3:15 ` Shawn Guo [not found] ` <CAAQ0ZWSj3-wWm0HGBPwAhCzZvSxDob6w2cgb1FWzp+K=Ev5BNA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> 0 siblings, 1 reply; 7+ messages in thread From: Shawn Guo @ 2012-04-20 3:15 UTC (permalink / raw) To: Fabio Estevam Cc: Fabio Estevam, snijsure-4jo+YWezP1RWk0Htik3J/w, marek.vasut-Re5JQEeQqe8AvxtiuMwx3w, kernel-bIcnvbaLZ9MEGnE8C9+IrQ, spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f, Chris Ball On 20 April 2012 11:07, Fabio Estevam <festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > On Thu, Apr 19, 2012 at 11:59 PM, Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote: > >> We are trying to remove <mach/*.h> from drivers. And this patch moves >> to the opposite direction. > > Where would be a good location for ssp-regs.h then? > include/linux/fsl? Regards, Shawn ------------------------------------------------------------------------------ For Developers, A Lot Can Happen In A Second. Boundary is the first to Know...and Tell You. Monitor Your Applications in Ultra-Fine Resolution. Try it FREE! http://p.sf.net/sfu/Boundary-d2dvs2 ^ permalink raw reply [flat|nested] 7+ messages in thread
[parent not found: <CAAQ0ZWSj3-wWm0HGBPwAhCzZvSxDob6w2cgb1FWzp+K=Ev5BNA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>]
* Re: [PATCH 1/2] ARM: mxs: Provide a common header file for SSP controller [not found] ` <CAAQ0ZWSj3-wWm0HGBPwAhCzZvSxDob6w2cgb1FWzp+K=Ev5BNA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> @ 2012-04-20 16:30 ` Marek Vasut 0 siblings, 0 replies; 7+ messages in thread From: Marek Vasut @ 2012-04-20 16:30 UTC (permalink / raw) To: Shawn Guo Cc: Fabio Estevam, snijsure-4jo+YWezP1RWk0Htik3J/w, kernel-bIcnvbaLZ9MEGnE8C9+IrQ, spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f, Chris Ball, Fabio Estevam Dear Shawn Guo, > On 20 April 2012 11:07, Fabio Estevam <festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote: > > On Thu, Apr 19, 2012 at 11:59 PM, Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote: > >> We are trying to remove <mach/*.h> from drivers. And this patch moves > >> to the opposite direction. > > > > Where would be a good location for ssp-regs.h then? > > include/linux/fsl? Funny, I recall how linux folks were moving all include/asm-arm/ stuff into arch/arm and now you're going on the opposite direction ? :-) > > Regards, > Shawn Best regards, Marek Vasut ------------------------------------------------------------------------------ For Developers, A Lot Can Happen In A Second. Boundary is the first to Know...and Tell You. Monitor Your Applications in Ultra-Fine Resolution. Try it FREE! http://p.sf.net/sfu/Boundary-d2dvs2 ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2012-04-20 16:30 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-04-19 0:30 [PATCH 1/2] ARM: mxs: Provide a common header file for SSP controller Fabio Estevam [not found] ` <1334795434-8780-1-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 2012-04-19 0:30 ` [PATCH 2/2] spi: Add initial support for spi-mxs Fabio Estevam [not found] ` <1334795434-8780-2-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 2012-04-20 4:12 ` Shawn Guo 2012-04-20 2:59 ` [PATCH 1/2] ARM: mxs: Provide a common header file for SSP controller Shawn Guo [not found] ` <20120420025918.GI22219-rvtDTF3kK1ictlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org> 2012-04-20 3:07 ` Fabio Estevam [not found] ` <CAOMZO5AOmAA93LLQnzHrOf+prH5rmNw7C-ZSpYzpKGUrZ74G1g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> 2012-04-20 3:15 ` Shawn Guo [not found] ` <CAAQ0ZWSj3-wWm0HGBPwAhCzZvSxDob6w2cgb1FWzp+K=Ev5BNA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> 2012-04-20 16:30 ` Marek Vasut
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).