Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
8266bdd2
Commit
8266bdd2
authored
Nov 14, 2012
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'spi-tegra' into spi-next
parents
19f15f0e
8528547b
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
700 additions
and
0 deletions
+700
-0
Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt
...ntation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt
+26
-0
drivers/spi/Kconfig
drivers/spi/Kconfig
+8
-0
drivers/spi/Makefile
drivers/spi/Makefile
+1
-0
drivers/spi/spi-tegra20-sflash.c
drivers/spi/spi-tegra20-sflash.c
+665
-0
No files found.
Documentation/devicetree/bindings/spi/nvidia,tegra20-sflash.txt
0 → 100644
View file @
8266bdd2
NVIDIA Tegra20 SFLASH controller.
Required properties:
- compatible : should be "nvidia,tegra20-sflash".
- reg: Should contain SFLASH registers location and length.
- interrupts: Should contain SFLASH interrupts.
- nvidia,dma-request-selector : The Tegra DMA controller's phandle and
request selector for this SFLASH controller.
Recommended properties:
- spi-max-frequency: Definition as per
Documentation/devicetree/bindings/spi/spi-bus.txt
Example:
spi@7000d600 {
compatible = "nvidia,tegra20-sflash";
reg = <0x7000c380 0x80>;
interrupts = <0 39 0x04>;
nvidia,dma-request-selector = <&apbdma 16>;
spi-max-frequency = <25000000>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
drivers/spi/Kconfig
View file @
8266bdd2
...
@@ -385,6 +385,14 @@ config SPI_MXS
...
@@ -385,6 +385,14 @@ config SPI_MXS
help
help
SPI driver for Freescale MXS devices.
SPI driver for Freescale MXS devices.
config SPI_TEGRA20_SFLASH
tristate "Nvidia Tegra20 Serial flash Controller"
depends on ARCH_TEGRA
help
SPI driver for Nvidia Tegra20 Serial flash Controller interface.
The main usecase of this controller is to use spi flash as boot
device.
config SPI_TEGRA20_SLINK
config SPI_TEGRA20_SLINK
tristate "Nvidia Tegra20/Tegra30 SLINK Controller"
tristate "Nvidia Tegra20/Tegra30 SLINK Controller"
depends on ARCH_TEGRA && TEGRA20_APB_DMA
depends on ARCH_TEGRA && TEGRA20_APB_DMA
...
...
drivers/spi/Makefile
View file @
8266bdd2
...
@@ -60,6 +60,7 @@ obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
...
@@ -60,6 +60,7 @@ obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
obj-$(CONFIG_SPI_SH_SCI)
+=
spi-sh-sci.o
obj-$(CONFIG_SPI_SH_SCI)
+=
spi-sh-sci.o
obj-$(CONFIG_SPI_SIRF)
+=
spi-sirf.o
obj-$(CONFIG_SPI_SIRF)
+=
spi-sirf.o
obj-$(CONFIG_SPI_STMP3XXX)
+=
spi-stmp.o
obj-$(CONFIG_SPI_STMP3XXX)
+=
spi-stmp.o
obj-$(CONFIG_SPI_TEGRA20_SFLASH)
+=
spi-tegra20-sflash.o
obj-$(CONFIG_SPI_TEGRA20_SLINK)
+=
spi-tegra20-slink.o
obj-$(CONFIG_SPI_TEGRA20_SLINK)
+=
spi-tegra20-slink.o
obj-$(CONFIG_SPI_TI_SSP)
+=
spi-ti-ssp.o
obj-$(CONFIG_SPI_TI_SSP)
+=
spi-ti-ssp.o
obj-$(CONFIG_SPI_TLE62X0)
+=
spi-tle62x0.o
obj-$(CONFIG_SPI_TLE62X0)
+=
spi-tle62x0.o
...
...
drivers/spi/spi-tegra20-sflash.c
0 → 100644
View file @
8266bdd2
/*
* SPI driver for Nvidia's Tegra20 Serial Flash Controller.
*
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
*
* Author: Laxman Dewangan <ldewangan@nvidia.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-tegra.h>
#include <mach/clk.h>
#define SPI_COMMAND 0x000
#define SPI_GO BIT(30)
#define SPI_M_S BIT(28)
#define SPI_ACTIVE_SCLK_MASK (0x3 << 26)
#define SPI_ACTIVE_SCLK_DRIVE_LOW (0 << 26)
#define SPI_ACTIVE_SCLK_DRIVE_HIGH (1 << 26)
#define SPI_ACTIVE_SCLK_PULL_LOW (2 << 26)
#define SPI_ACTIVE_SCLK_PULL_HIGH (3 << 26)
#define SPI_CK_SDA_FALLING (1 << 21)
#define SPI_CK_SDA_RISING (0 << 21)
#define SPI_CK_SDA_MASK (1 << 21)
#define SPI_ACTIVE_SDA (0x3 << 18)
#define SPI_ACTIVE_SDA_DRIVE_LOW (0 << 18)
#define SPI_ACTIVE_SDA_DRIVE_HIGH (1 << 18)
#define SPI_ACTIVE_SDA_PULL_LOW (2 << 18)
#define SPI_ACTIVE_SDA_PULL_HIGH (3 << 18)
#define SPI_CS_POL_INVERT BIT(16)
#define SPI_TX_EN BIT(15)
#define SPI_RX_EN BIT(14)
#define SPI_CS_VAL_HIGH BIT(13)
#define SPI_CS_VAL_LOW 0x0
#define SPI_CS_SW BIT(12)
#define SPI_CS_HW 0x0
#define SPI_CS_DELAY_MASK (7 << 9)
#define SPI_CS3_EN BIT(8)
#define SPI_CS2_EN BIT(7)
#define SPI_CS1_EN BIT(6)
#define SPI_CS0_EN BIT(5)
#define SPI_CS_MASK (SPI_CS3_EN | SPI_CS2_EN | \
SPI_CS1_EN | SPI_CS0_EN)
#define SPI_BIT_LENGTH(x) (((x) & 0x1f) << 0)
#define SPI_MODES (SPI_ACTIVE_SCLK_MASK | SPI_CK_SDA_MASK)
#define SPI_STATUS 0x004
#define SPI_BSY BIT(31)
#define SPI_RDY BIT(30)
#define SPI_TXF_FLUSH BIT(29)
#define SPI_RXF_FLUSH BIT(28)
#define SPI_RX_UNF BIT(27)
#define SPI_TX_OVF BIT(26)
#define SPI_RXF_EMPTY BIT(25)
#define SPI_RXF_FULL BIT(24)
#define SPI_TXF_EMPTY BIT(23)
#define SPI_TXF_FULL BIT(22)
#define SPI_BLK_CNT(count) (((count) & 0xffff) + 1)
#define SPI_FIFO_ERROR (SPI_RX_UNF | SPI_TX_OVF)
#define SPI_FIFO_EMPTY (SPI_TX_EMPTY | SPI_RX_EMPTY)
#define SPI_RX_CMP 0x8
#define SPI_DMA_CTL 0x0C
#define SPI_DMA_EN BIT(31)
#define SPI_IE_RXC BIT(27)
#define SPI_IE_TXC BIT(26)
#define SPI_PACKED BIT(20)
#define SPI_RX_TRIG_MASK (0x3 << 18)
#define SPI_RX_TRIG_1W (0x0 << 18)
#define SPI_RX_TRIG_4W (0x1 << 18)
#define SPI_TX_TRIG_MASK (0x3 << 16)
#define SPI_TX_TRIG_1W (0x0 << 16)
#define SPI_TX_TRIG_4W (0x1 << 16)
#define SPI_DMA_BLK_COUNT(count) (((count) - 1) & 0xFFFF);
#define SPI_TX_FIFO 0x10
#define SPI_RX_FIFO 0x20
#define DATA_DIR_TX (1 << 0)
#define DATA_DIR_RX (1 << 1)
#define MAX_CHIP_SELECT 4
#define SPI_FIFO_DEPTH 4
#define SPI_DMA_TIMEOUT (msecs_to_jiffies(1000))
struct
tegra_sflash_data
{
struct
device
*
dev
;
struct
spi_master
*
master
;
spinlock_t
lock
;
struct
clk
*
clk
;
void
__iomem
*
base
;
unsigned
irq
;
u32
spi_max_frequency
;
u32
cur_speed
;
struct
spi_device
*
cur_spi
;
unsigned
cur_pos
;
unsigned
cur_len
;
unsigned
bytes_per_word
;
unsigned
cur_direction
;
unsigned
curr_xfer_words
;
unsigned
cur_rx_pos
;
unsigned
cur_tx_pos
;
u32
tx_status
;
u32
rx_status
;
u32
status_reg
;
u32
def_command_reg
;
u32
command_reg
;
u32
dma_control_reg
;
struct
completion
xfer_completion
;
struct
spi_transfer
*
curr_xfer
;
};
static
int
tegra_sflash_runtime_suspend
(
struct
device
*
dev
);
static
int
tegra_sflash_runtime_resume
(
struct
device
*
dev
);
static
inline
unsigned
long
tegra_sflash_readl
(
struct
tegra_sflash_data
*
tsd
,
unsigned
long
reg
)
{
return
readl
(
tsd
->
base
+
reg
);
}
static
inline
void
tegra_sflash_writel
(
struct
tegra_sflash_data
*
tsd
,
unsigned
long
val
,
unsigned
long
reg
)
{
writel
(
val
,
tsd
->
base
+
reg
);
}
static
void
tegra_sflash_clear_status
(
struct
tegra_sflash_data
*
tsd
)
{
/* Write 1 to clear status register */
tegra_sflash_writel
(
tsd
,
SPI_RDY
|
SPI_FIFO_ERROR
,
SPI_STATUS
);
}
static
unsigned
tegra_sflash_calculate_curr_xfer_param
(
struct
spi_device
*
spi
,
struct
tegra_sflash_data
*
tsd
,
struct
spi_transfer
*
t
)
{
unsigned
remain_len
=
t
->
len
-
tsd
->
cur_pos
;
unsigned
max_word
;
tsd
->
bytes_per_word
=
(
t
->
bits_per_word
-
1
)
/
8
+
1
;
max_word
=
remain_len
/
tsd
->
bytes_per_word
;
if
(
max_word
>
SPI_FIFO_DEPTH
)
max_word
=
SPI_FIFO_DEPTH
;
tsd
->
curr_xfer_words
=
max_word
;
return
max_word
;
}
static
unsigned
tegra_sflash_fill_tx_fifo_from_client_txbuf
(
struct
tegra_sflash_data
*
tsd
,
struct
spi_transfer
*
t
)
{
unsigned
nbytes
;
unsigned
long
status
;
unsigned
max_n_32bit
=
tsd
->
curr_xfer_words
;
u8
*
tx_buf
=
(
u8
*
)
t
->
tx_buf
+
tsd
->
cur_tx_pos
;
if
(
max_n_32bit
>
SPI_FIFO_DEPTH
)
max_n_32bit
=
SPI_FIFO_DEPTH
;
nbytes
=
max_n_32bit
*
tsd
->
bytes_per_word
;
status
=
tegra_sflash_readl
(
tsd
,
SPI_STATUS
);
while
(
!
(
status
&
SPI_TXF_FULL
))
{
int
i
;
unsigned
int
x
=
0
;
for
(
i
=
0
;
nbytes
&&
(
i
<
tsd
->
bytes_per_word
);
i
++
,
nbytes
--
)
x
|=
((
*
tx_buf
++
)
<<
i
*
8
);
tegra_sflash_writel
(
tsd
,
x
,
SPI_TX_FIFO
);
if
(
!
nbytes
)
break
;
status
=
tegra_sflash_readl
(
tsd
,
SPI_STATUS
);
}
tsd
->
cur_tx_pos
+=
max_n_32bit
*
tsd
->
bytes_per_word
;
return
max_n_32bit
;
}
static
int
tegra_sflash_read_rx_fifo_to_client_rxbuf
(
struct
tegra_sflash_data
*
tsd
,
struct
spi_transfer
*
t
)
{
unsigned
long
status
;
unsigned
int
read_words
=
0
;
u8
*
rx_buf
=
(
u8
*
)
t
->
rx_buf
+
tsd
->
cur_rx_pos
;
status
=
tegra_sflash_readl
(
tsd
,
SPI_STATUS
);
while
(
!
(
status
&
SPI_RXF_EMPTY
))
{
int
i
;
unsigned
long
x
;
x
=
tegra_sflash_readl
(
tsd
,
SPI_RX_FIFO
);
for
(
i
=
0
;
(
i
<
tsd
->
bytes_per_word
);
i
++
)
*
rx_buf
++
=
(
x
>>
(
i
*
8
))
&
0xFF
;
read_words
++
;
status
=
tegra_sflash_readl
(
tsd
,
SPI_STATUS
);
}
tsd
->
cur_rx_pos
+=
read_words
*
tsd
->
bytes_per_word
;
return
0
;
}
static
int
tegra_sflash_start_cpu_based_transfer
(
struct
tegra_sflash_data
*
tsd
,
struct
spi_transfer
*
t
)
{
unsigned
long
val
=
0
;
unsigned
cur_words
;
if
(
tsd
->
cur_direction
&
DATA_DIR_TX
)
val
|=
SPI_IE_TXC
;
if
(
tsd
->
cur_direction
&
DATA_DIR_RX
)
val
|=
SPI_IE_RXC
;
tegra_sflash_writel
(
tsd
,
val
,
SPI_DMA_CTL
);
tsd
->
dma_control_reg
=
val
;
if
(
tsd
->
cur_direction
&
DATA_DIR_TX
)
cur_words
=
tegra_sflash_fill_tx_fifo_from_client_txbuf
(
tsd
,
t
);
else
cur_words
=
tsd
->
curr_xfer_words
;
val
|=
SPI_DMA_BLK_COUNT
(
cur_words
);
tegra_sflash_writel
(
tsd
,
val
,
SPI_DMA_CTL
);
tsd
->
dma_control_reg
=
val
;
val
|=
SPI_DMA_EN
;
tegra_sflash_writel
(
tsd
,
val
,
SPI_DMA_CTL
);
return
0
;
}
static
int
tegra_sflash_start_transfer_one
(
struct
spi_device
*
spi
,
struct
spi_transfer
*
t
,
bool
is_first_of_msg
,
bool
is_single_xfer
)
{
struct
tegra_sflash_data
*
tsd
=
spi_master_get_devdata
(
spi
->
master
);
u32
speed
;
unsigned
long
command
;
speed
=
t
->
speed_hz
?
t
->
speed_hz
:
spi
->
max_speed_hz
;
if
(
!
speed
)
speed
=
tsd
->
spi_max_frequency
;
if
(
speed
!=
tsd
->
cur_speed
)
{
clk_set_rate
(
tsd
->
clk
,
speed
);
tsd
->
cur_speed
=
speed
;
}
tsd
->
cur_spi
=
spi
;
tsd
->
cur_pos
=
0
;
tsd
->
cur_rx_pos
=
0
;
tsd
->
cur_tx_pos
=
0
;
tsd
->
curr_xfer
=
t
;
tegra_sflash_calculate_curr_xfer_param
(
spi
,
tsd
,
t
);
if
(
is_first_of_msg
)
{
command
=
tsd
->
def_command_reg
;
command
|=
SPI_BIT_LENGTH
(
t
->
bits_per_word
-
1
);
command
|=
SPI_CS_VAL_HIGH
;
command
&=
~
SPI_MODES
;
if
(
spi
->
mode
&
SPI_CPHA
)
command
|=
SPI_CK_SDA_FALLING
;
if
(
spi
->
mode
&
SPI_CPOL
)
command
|=
SPI_ACTIVE_SCLK_DRIVE_HIGH
;
else
command
|=
SPI_ACTIVE_SCLK_DRIVE_LOW
;
command
|=
SPI_CS0_EN
<<
spi
->
chip_select
;
}
else
{
command
=
tsd
->
command_reg
;
command
&=
~
SPI_BIT_LENGTH
(
~
0
);
command
|=
SPI_BIT_LENGTH
(
t
->
bits_per_word
-
1
);
command
&=
~
(
SPI_RX_EN
|
SPI_TX_EN
);
}
tsd
->
cur_direction
=
0
;
if
(
t
->
rx_buf
)
{
command
|=
SPI_RX_EN
;
tsd
->
cur_direction
|=
DATA_DIR_RX
;
}
if
(
t
->
tx_buf
)
{
command
|=
SPI_TX_EN
;
tsd
->
cur_direction
|=
DATA_DIR_TX
;
}
tegra_sflash_writel
(
tsd
,
command
,
SPI_COMMAND
);
tsd
->
command_reg
=
command
;
return
tegra_sflash_start_cpu_based_transfer
(
tsd
,
t
);
}
static
int
tegra_sflash_transfer_one_message
(
struct
spi_master
*
master
,
struct
spi_message
*
msg
)
{
bool
is_first_msg
=
true
;
int
single_xfer
;
struct
tegra_sflash_data
*
tsd
=
spi_master_get_devdata
(
master
);
struct
spi_transfer
*
xfer
;
struct
spi_device
*
spi
=
msg
->
spi
;
int
ret
;
ret
=
pm_runtime_get_sync
(
tsd
->
dev
);
if
(
ret
<
0
)
{
dev_err
(
tsd
->
dev
,
"pm_runtime_get() failed, err = %d
\n
"
,
ret
);
return
ret
;
}
msg
->
status
=
0
;
msg
->
actual_length
=
0
;
single_xfer
=
list_is_singular
(
&
msg
->
transfers
);
list_for_each_entry
(
xfer
,
&
msg
->
transfers
,
transfer_list
)
{
INIT_COMPLETION
(
tsd
->
xfer_completion
);
ret
=
tegra_sflash_start_transfer_one
(
spi
,
xfer
,
is_first_msg
,
single_xfer
);
if
(
ret
<
0
)
{
dev_err
(
tsd
->
dev
,
"spi can not start transfer, err %d
\n
"
,
ret
);
goto
exit
;
}
is_first_msg
=
false
;
ret
=
wait_for_completion_timeout
(
&
tsd
->
xfer_completion
,
SPI_DMA_TIMEOUT
);
if
(
WARN_ON
(
ret
==
0
))
{
dev_err
(
tsd
->
dev
,
"spi trasfer timeout, err %d
\n
"
,
ret
);
ret
=
-
EIO
;
goto
exit
;
}
if
(
tsd
->
tx_status
||
tsd
->
rx_status
)
{
dev_err
(
tsd
->
dev
,
"Error in Transfer
\n
"
);
ret
=
-
EIO
;
goto
exit
;
}
msg
->
actual_length
+=
xfer
->
len
;
if
(
xfer
->
cs_change
&&
xfer
->
delay_usecs
)
{
tegra_sflash_writel
(
tsd
,
tsd
->
def_command_reg
,
SPI_COMMAND
);
udelay
(
xfer
->
delay_usecs
);
}
}
ret
=
0
;
exit:
tegra_sflash_writel
(
tsd
,
tsd
->
def_command_reg
,
SPI_COMMAND
);
msg
->
status
=
ret
;
spi_finalize_current_message
(
master
);
pm_runtime_put
(
tsd
->
dev
);
return
ret
;
}
static
irqreturn_t
handle_cpu_based_xfer
(
struct
tegra_sflash_data
*
tsd
)
{
struct
spi_transfer
*
t
=
tsd
->
curr_xfer
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
tsd
->
lock
,
flags
);
if
(
tsd
->
tx_status
||
tsd
->
rx_status
||
(
tsd
->
status_reg
&
SPI_BSY
))
{
dev_err
(
tsd
->
dev
,
"CpuXfer ERROR bit set 0x%x
\n
"
,
tsd
->
status_reg
);
dev_err
(
tsd
->
dev
,
"CpuXfer 0x%08x:0x%08x
\n
"
,
tsd
->
command_reg
,
tsd
->
dma_control_reg
);
tegra_periph_reset_assert
(
tsd
->
clk
);
udelay
(
2
);
tegra_periph_reset_deassert
(
tsd
->
clk
);
complete
(
&
tsd
->
xfer_completion
);
goto
exit
;
}
if
(
tsd
->
cur_direction
&
DATA_DIR_RX
)
tegra_sflash_read_rx_fifo_to_client_rxbuf
(
tsd
,
t
);
if
(
tsd
->
cur_direction
&
DATA_DIR_TX
)
tsd
->
cur_pos
=
tsd
->
cur_tx_pos
;
else
tsd
->
cur_pos
=
tsd
->
cur_rx_pos
;
if
(
tsd
->
cur_pos
==
t
->
len
)
{
complete
(
&
tsd
->
xfer_completion
);
goto
exit
;
}
tegra_sflash_calculate_curr_xfer_param
(
tsd
->
cur_spi
,
tsd
,
t
);
tegra_sflash_start_cpu_based_transfer
(
tsd
,
t
);
exit:
spin_unlock_irqrestore
(
&
tsd
->
lock
,
flags
);
return
IRQ_HANDLED
;
}
static
irqreturn_t
tegra_sflash_isr
(
int
irq
,
void
*
context_data
)
{
struct
tegra_sflash_data
*
tsd
=
context_data
;
tsd
->
status_reg
=
tegra_sflash_readl
(
tsd
,
SPI_STATUS
);
if
(
tsd
->
cur_direction
&
DATA_DIR_TX
)
tsd
->
tx_status
=
tsd
->
status_reg
&
SPI_TX_OVF
;
if
(
tsd
->
cur_direction
&
DATA_DIR_RX
)
tsd
->
rx_status
=
tsd
->
status_reg
&
SPI_RX_UNF
;
tegra_sflash_clear_status
(
tsd
);
return
handle_cpu_based_xfer
(
tsd
);
}
static
struct
tegra_spi_platform_data
*
tegra_sflash_parse_dt
(
struct
platform_device
*
pdev
)
{
struct
tegra_spi_platform_data
*
pdata
;
struct
device_node
*
np
=
pdev
->
dev
.
of_node
;
u32
max_freq
;
pdata
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
pdata
),
GFP_KERNEL
);
if
(
!
pdata
)
{
dev_err
(
&
pdev
->
dev
,
"Memory alloc for pdata failed
\n
"
);
return
NULL
;
}
if
(
!
of_property_read_u32
(
np
,
"spi-max-frequency"
,
&
max_freq
))
pdata
->
spi_max_frequency
=
max_freq
;
return
pdata
;
}
static
struct
of_device_id
tegra_sflash_of_match
[]
__devinitconst
=
{
{
.
compatible
=
"nvidia,tegra20-sflash"
,
},
{}
};
MODULE_DEVICE_TABLE
(
of
,
tegra_sflash_of_match
);
static
int
__devinit
tegra_sflash_probe
(
struct
platform_device
*
pdev
)
{
struct
spi_master
*
master
;
struct
tegra_sflash_data
*
tsd
;
struct
resource
*
r
;
struct
tegra_spi_platform_data
*
pdata
=
pdev
->
dev
.
platform_data
;
int
ret
;
const
struct
of_device_id
*
match
;
match
=
of_match_device
(
of_match_ptr
(
tegra_sflash_of_match
),
&
pdev
->
dev
);
if
(
!
match
)
{
dev_err
(
&
pdev
->
dev
,
"Error: No device match found
\n
"
);
return
-
ENODEV
;
}
if
(
!
pdata
&&
pdev
->
dev
.
of_node
)
pdata
=
tegra_sflash_parse_dt
(
pdev
);
if
(
!
pdata
)
{
dev_err
(
&
pdev
->
dev
,
"No platform data, exiting
\n
"
);
return
-
ENODEV
;
}
if
(
!
pdata
->
spi_max_frequency
)
pdata
->
spi_max_frequency
=
25000000
;
/* 25MHz */
master
=
spi_alloc_master
(
&
pdev
->
dev
,
sizeof
(
*
tsd
));
if
(
!
master
)
{
dev_err
(
&
pdev
->
dev
,
"master allocation failed
\n
"
);
return
-
ENOMEM
;
}
/* the spi->mode bits understood by this driver: */
master
->
mode_bits
=
SPI_CPOL
|
SPI_CPHA
;
master
->
transfer_one_message
=
tegra_sflash_transfer_one_message
;
master
->
num_chipselect
=
MAX_CHIP_SELECT
;
master
->
bus_num
=
-
1
;
dev_set_drvdata
(
&
pdev
->
dev
,
master
);
tsd
=
spi_master_get_devdata
(
master
);
tsd
->
master
=
master
;
tsd
->
dev
=
&
pdev
->
dev
;
spin_lock_init
(
&
tsd
->
lock
);
r
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
if
(
!
r
)
{
dev_err
(
&
pdev
->
dev
,
"No IO memory resource
\n
"
);
ret
=
-
ENODEV
;
goto
exit_free_master
;
}
tsd
->
base
=
devm_request_and_ioremap
(
&
pdev
->
dev
,
r
);
if
(
!
tsd
->
base
)
{
dev_err
(
&
pdev
->
dev
,
"Cannot request memregion/iomap dma address
\n
"
);
ret
=
-
EADDRNOTAVAIL
;
goto
exit_free_master
;
}
tsd
->
irq
=
platform_get_irq
(
pdev
,
0
);
ret
=
request_irq
(
tsd
->
irq
,
tegra_sflash_isr
,
0
,
dev_name
(
&
pdev
->
dev
),
tsd
);
if
(
ret
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"Failed to register ISR for IRQ %d
\n
"
,
tsd
->
irq
);
goto
exit_free_master
;
}
tsd
->
clk
=
devm_clk_get
(
&
pdev
->
dev
,
"spi"
);
if
(
IS_ERR
(
tsd
->
clk
))
{
dev_err
(
&
pdev
->
dev
,
"can not get clock
\n
"
);
ret
=
PTR_ERR
(
tsd
->
clk
);
goto
exit_free_irq
;
}
tsd
->
spi_max_frequency
=
pdata
->
spi_max_frequency
;
init_completion
(
&
tsd
->
xfer_completion
);
pm_runtime_enable
(
&
pdev
->
dev
);
if
(
!
pm_runtime_enabled
(
&
pdev
->
dev
))
{
ret
=
tegra_sflash_runtime_resume
(
&
pdev
->
dev
);
if
(
ret
)
goto
exit_pm_disable
;
}
ret
=
pm_runtime_get_sync
(
&
pdev
->
dev
);
if
(
ret
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"pm runtime get failed, e = %d
\n
"
,
ret
);
goto
exit_pm_disable
;
}
/* Reset controller */
tegra_periph_reset_assert
(
tsd
->
clk
);
udelay
(
2
);
tegra_periph_reset_deassert
(
tsd
->
clk
);
tsd
->
def_command_reg
=
SPI_M_S
|
SPI_CS_SW
;
tegra_sflash_writel
(
tsd
,
tsd
->
def_command_reg
,
SPI_COMMAND
);
pm_runtime_put
(
&
pdev
->
dev
);
master
->
dev
.
of_node
=
pdev
->
dev
.
of_node
;
ret
=
spi_register_master
(
master
);
if
(
ret
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"can not register to master err %d
\n
"
,
ret
);
goto
exit_pm_disable
;
}
return
ret
;
exit_pm_disable:
pm_runtime_disable
(
&
pdev
->
dev
);
if
(
!
pm_runtime_status_suspended
(
&
pdev
->
dev
))
tegra_sflash_runtime_suspend
(
&
pdev
->
dev
);
exit_free_irq:
free_irq
(
tsd
->
irq
,
tsd
);
exit_free_master:
spi_master_put
(
master
);
return
ret
;
}
static
int
__devexit
tegra_sflash_remove
(
struct
platform_device
*
pdev
)
{
struct
spi_master
*
master
=
dev_get_drvdata
(
&
pdev
->
dev
);
struct
tegra_sflash_data
*
tsd
=
spi_master_get_devdata
(
master
);
free_irq
(
tsd
->
irq
,
tsd
);
spi_unregister_master
(
master
);
pm_runtime_disable
(
&
pdev
->
dev
);
if
(
!
pm_runtime_status_suspended
(
&
pdev
->
dev
))
tegra_sflash_runtime_suspend
(
&
pdev
->
dev
);
return
0
;
}
#ifdef CONFIG_PM_SLEEP
static
int
tegra_sflash_suspend
(
struct
device
*
dev
)
{
struct
spi_master
*
master
=
dev_get_drvdata
(
dev
);
return
spi_master_suspend
(
master
);
}
static
int
tegra_sflash_resume
(
struct
device
*
dev
)
{
struct
spi_master
*
master
=
dev_get_drvdata
(
dev
);
struct
tegra_sflash_data
*
tsd
=
spi_master_get_devdata
(
master
);
int
ret
;
ret
=
pm_runtime_get_sync
(
dev
);
if
(
ret
<
0
)
{
dev_err
(
dev
,
"pm runtime failed, e = %d
\n
"
,
ret
);
return
ret
;
}
tegra_sflash_writel
(
tsd
,
tsd
->
command_reg
,
SPI_COMMAND
);
pm_runtime_put
(
dev
);
return
spi_master_resume
(
master
);
}
#endif
static
int
tegra_sflash_runtime_suspend
(
struct
device
*
dev
)
{
struct
spi_master
*
master
=
dev_get_drvdata
(
dev
);
struct
tegra_sflash_data
*
tsd
=
spi_master_get_devdata
(
master
);
/* Flush all write which are in PPSB queue by reading back */
tegra_sflash_readl
(
tsd
,
SPI_COMMAND
);
clk_disable_unprepare
(
tsd
->
clk
);
return
0
;
}
static
int
tegra_sflash_runtime_resume
(
struct
device
*
dev
)
{
struct
spi_master
*
master
=
dev_get_drvdata
(
dev
);
struct
tegra_sflash_data
*
tsd
=
spi_master_get_devdata
(
master
);
int
ret
;
ret
=
clk_prepare_enable
(
tsd
->
clk
);
if
(
ret
<
0
)
{
dev_err
(
tsd
->
dev
,
"clk_prepare failed: %d
\n
"
,
ret
);
return
ret
;
}
return
0
;
}
static
const
struct
dev_pm_ops
slink_pm_ops
=
{
SET_RUNTIME_PM_OPS
(
tegra_sflash_runtime_suspend
,
tegra_sflash_runtime_resume
,
NULL
)
SET_SYSTEM_SLEEP_PM_OPS
(
tegra_sflash_suspend
,
tegra_sflash_resume
)
};
static
struct
platform_driver
tegra_sflash_driver
=
{
.
driver
=
{
.
name
=
"spi-tegra-sflash"
,
.
owner
=
THIS_MODULE
,
.
pm
=
&
slink_pm_ops
,
.
of_match_table
=
of_match_ptr
(
tegra_sflash_of_match
),
},
.
probe
=
tegra_sflash_probe
,
.
remove
=
__devexit_p
(
tegra_sflash_remove
),
};
module_platform_driver
(
tegra_sflash_driver
);
MODULE_ALIAS
(
"platform:spi-tegra-sflash"
);
MODULE_DESCRIPTION
(
"NVIDIA Tegra20 Serial Flash Controller Driver"
);
MODULE_AUTHOR
(
"Laxman Dewangan <ldewangan@nvidia.com>"
);
MODULE_LICENSE
(
"GPL v2"
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment