* [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
* [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
* 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
* 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
* 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
* 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] ` <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).