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
Kirill Smelkov
linux
Commits
74ff666b
Commit
74ff666b
authored
Dec 20, 2018
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branches 'spi/topic/mem' and 'spi/topic/mtd' into spi-next
parents
b3fc4e0e
aa167f3f
2a9d92fb
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
488 additions
and
413 deletions
+488
-413
Documentation/devicetree/bindings/spi/atmel-quadspi.txt
Documentation/devicetree/bindings/spi/atmel-quadspi.txt
+0
-0
drivers/mtd/spi-nor/Kconfig
drivers/mtd/spi-nor/Kconfig
+0
-9
drivers/mtd/spi-nor/Makefile
drivers/mtd/spi-nor/Makefile
+0
-1
drivers/spi/Kconfig
drivers/spi/Kconfig
+9
-0
drivers/spi/Makefile
drivers/spi/Makefile
+1
-0
drivers/spi/atmel-quadspi.c
drivers/spi/atmel-quadspi.c
+148
-380
drivers/spi/spi-mem.c
drivers/spi/spi-mem.c
+247
-22
include/linux/spi/spi-mem.h
include/linux/spi/spi-mem.h
+83
-1
No files found.
Documentation/devicetree/bindings/
mtd
/atmel-quadspi.txt
→
Documentation/devicetree/bindings/
spi
/atmel-quadspi.txt
View file @
74ff666b
File moved
drivers/mtd/spi-nor/Kconfig
View file @
74ff666b
...
...
@@ -39,15 +39,6 @@ config SPI_ASPEED_SMC
and support for the SPI flash memory controller (SPI) for
the host firmware. The implementation only supports SPI NOR.
config SPI_ATMEL_QUADSPI
tristate "Atmel Quad SPI Controller"
depends on ARCH_AT91 || (ARM && COMPILE_TEST)
depends on OF && HAS_IOMEM
help
This enables support for the Quad SPI controller in master mode.
This driver does not support generic SPI. The implementation only
supports SPI NOR.
config SPI_CADENCE_QUADSPI
tristate "Cadence Quad SPI controller"
depends on OF && (ARM || ARM64 || COMPILE_TEST)
...
...
drivers/mtd/spi-nor/Makefile
View file @
74ff666b
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MTD_SPI_NOR)
+=
spi-nor.o
obj-$(CONFIG_SPI_ASPEED_SMC)
+=
aspeed-smc.o
obj-$(CONFIG_SPI_ATMEL_QUADSPI)
+=
atmel-quadspi.o
obj-$(CONFIG_SPI_CADENCE_QUADSPI)
+=
cadence-quadspi.o
obj-$(CONFIG_SPI_FSL_QUADSPI)
+=
fsl-quadspi.o
obj-$(CONFIG_SPI_HISI_SFC)
+=
hisi-sfc.o
...
...
drivers/spi/Kconfig
View file @
74ff666b
...
...
@@ -91,6 +91,15 @@ config SPI_AT91_USART
This selects a driver for the AT91 USART Controller as SPI Master,
present on AT91 and SAMA5 SoC series.
config SPI_ATMEL_QUADSPI
tristate "Atmel Quad SPI Controller"
depends on ARCH_AT91 || (ARM && COMPILE_TEST && !ARCH_EBSA110)
depends on OF && HAS_IOMEM
help
This enables support for the Quad SPI controller in master mode.
This driver does not support generic SPI. The implementation only
supports spi-mem interface.
config SPI_AU1550
tristate "Au1550/Au1200/Au1300 SPI Controller"
depends on MIPS_ALCHEMY
...
...
drivers/spi/Makefile
View file @
74ff666b
...
...
@@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
obj-$(CONFIG_SPI_ALTERA)
+=
spi-altera.o
obj-$(CONFIG_SPI_ARMADA_3700)
+=
spi-armada-3700.o
obj-$(CONFIG_SPI_ATMEL)
+=
spi-atmel.o
obj-$(CONFIG_SPI_ATMEL_QUADSPI)
+=
atmel-quadspi.o
obj-$(CONFIG_SPI_AT91_USART)
+=
spi-at91-usart.o
obj-$(CONFIG_SPI_ATH79)
+=
spi-ath79.o
obj-$(CONFIG_SPI_AU1550)
+=
spi-au1550.o
...
...
drivers/
mtd/spi-nor
/atmel-quadspi.c
→
drivers/
spi
/atmel-quadspi.c
View file @
74ff666b
...
...
@@ -2,8 +2,10 @@
* Driver for Atmel QSPI Controller
*
* Copyright (C) 2015 Atmel Corporation
* Copyright (C) 2018 Cryptera A/S
*
* Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
* Author: Piotr Bugalski <bugalski.piotr@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
...
...
@@ -27,14 +29,10 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/spi-nor.h>
#include <linux/platform_data/atmel.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/
gpio/consumer
.h>
#include <linux/
spi/spi-mem
.h>
/* QSPI register offsets */
#define QSPI_CR 0x0000
/* Control Register */
...
...
@@ -67,7 +65,7 @@
#define QSPI_CR_LASTXFER BIT(24)
/* Bitfields in QSPI_MR (Mode Register) */
#define QSPI_MR_S
S
M BIT(0)
#define QSPI_MR_S
M
M BIT(0)
#define QSPI_MR_LLB BIT(1)
#define QSPI_MR_WDRBT BIT(2)
#define QSPI_MR_SMRM BIT(3)
...
...
@@ -157,33 +155,24 @@ struct atmel_qspi {
struct
clk
*
clk
;
struct
platform_device
*
pdev
;
u32
pending
;
struct
spi_nor
nor
;
u32
clk_rate
;
struct
completion
cmd_completion
;
};
struct
atmel_qspi_command
{
union
{
struct
{
u32
instruction
:
1
;
u32
address
:
3
;
u32
mode
:
1
;
u32
dummy
:
1
;
u32
data
:
1
;
u32
reserved
:
25
;
}
bits
;
u32
word
;
}
enable
;
u8
instruction
;
u8
mode
;
u8
num_mode_cycles
;
u8
num_dummy_cycles
;
u32
address
;
size_t
buf_len
;
const
void
*
tx_buf
;
void
*
rx_buf
;
struct
qspi_mode
{
u8
cmd_buswidth
;
u8
addr_buswidth
;
u8
data_buswidth
;
u32
config
;
};
static
const
struct
qspi_mode
sama5d2_qspi_modes
[]
=
{
{
1
,
1
,
1
,
QSPI_IFR_WIDTH_SINGLE_BIT_SPI
},
{
1
,
1
,
2
,
QSPI_IFR_WIDTH_DUAL_OUTPUT
},
{
1
,
1
,
4
,
QSPI_IFR_WIDTH_QUAD_OUTPUT
},
{
1
,
2
,
2
,
QSPI_IFR_WIDTH_DUAL_IO
},
{
1
,
4
,
4
,
QSPI_IFR_WIDTH_QUAD_IO
},
{
2
,
2
,
2
,
QSPI_IFR_WIDTH_DUAL_CMD
},
{
4
,
4
,
4
,
QSPI_IFR_WIDTH_QUAD_CMD
},
};
/* Register access functions */
...
...
@@ -197,246 +186,140 @@ static inline void qspi_writel(struct atmel_qspi *aq, u32 reg, u32 value)
writel_relaxed
(
value
,
aq
->
regs
+
reg
);
}
static
in
t
atmel_qspi_run_transfer
(
struct
atmel_qspi
*
aq
,
const
struct
atmel_qspi_command
*
cmd
)
static
in
line
bool
is_compatible
(
const
struct
spi_mem_op
*
op
,
const
struct
qspi_mode
*
mode
)
{
void
__iomem
*
ahb_mem
;
/* Then fallback to a PIO transfer (memcpy() DOES NOT work!) */
ahb_mem
=
aq
->
mem
;
if
(
cmd
->
enable
.
bits
.
address
)
ahb_mem
+=
cmd
->
address
;
if
(
cmd
->
tx_buf
)
_memcpy_toio
(
ahb_mem
,
cmd
->
tx_buf
,
cmd
->
buf_len
);
else
_memcpy_fromio
(
cmd
->
rx_buf
,
ahb_mem
,
cmd
->
buf_len
);
if
(
op
->
cmd
.
buswidth
!=
mode
->
cmd_buswidth
)
return
false
;
return
0
;
}
#ifdef DEBUG
static
void
atmel_qspi_debug_command
(
struct
atmel_qspi
*
aq
,
const
struct
atmel_qspi_command
*
cmd
,
u32
ifr
)
{
u8
cmd_buf
[
SPI_NOR_MAX_CMD_SIZE
];
size_t
len
=
0
;
int
i
;
if
(
op
->
addr
.
nbytes
&&
op
->
addr
.
buswidth
!=
mode
->
addr_buswidth
)
return
false
;
if
(
cmd
->
enable
.
bits
.
instruction
)
cmd_buf
[
len
++
]
=
cmd
->
instruction
;
if
(
op
->
data
.
nbytes
&&
op
->
data
.
buswidth
!=
mode
->
data_buswidth
)
return
false
;
for
(
i
=
cmd
->
enable
.
bits
.
address
-
1
;
i
>=
0
;
--
i
)
cmd_buf
[
len
++
]
=
(
cmd
->
address
>>
(
i
<<
3
))
&
0xff
;
return
true
;
}
if
(
cmd
->
enable
.
bits
.
mode
)
cmd_buf
[
len
++
]
=
cmd
->
mode
;
static
int
find_mode
(
const
struct
spi_mem_op
*
op
)
{
u32
i
;
if
(
cmd
->
enable
.
bits
.
dummy
)
{
int
num
=
cmd
->
num_dummy_cycles
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
sama5d2_qspi_modes
);
i
++
)
if
(
is_compatible
(
op
,
&
sama5d2_qspi_modes
[
i
]))
return
i
;
switch
(
ifr
&
QSPI_IFR_WIDTH_MASK
)
{
case
QSPI_IFR_WIDTH_SINGLE_BIT_SPI
:
case
QSPI_IFR_WIDTH_DUAL_OUTPUT
:
case
QSPI_IFR_WIDTH_QUAD_OUTPUT
:
num
>>=
3
;
break
;
case
QSPI_IFR_WIDTH_DUAL_IO
:
case
QSPI_IFR_WIDTH_DUAL_CMD
:
num
>>=
2
;
break
;
case
QSPI_IFR_WIDTH_QUAD_IO
:
case
QSPI_IFR_WIDTH_QUAD_CMD
:
num
>>=
1
;
break
;
default:
return
;
}
return
-
1
;
}
for
(
i
=
0
;
i
<
num
;
++
i
)
cmd_buf
[
len
++
]
=
0
;
}
static
bool
atmel_qspi_supports_op
(
struct
spi_mem
*
mem
,
const
struct
spi_mem_op
*
op
)
{
if
(
find_mode
(
op
)
<
0
)
return
false
;
/* Dump the SPI command */
print_hex_dump
(
KERN_DEBUG
,
"qspi cmd: "
,
DUMP_PREFIX_NONE
,
32
,
1
,
cmd_buf
,
len
,
false
);
/* special case not supported by hardware */
if
(
op
->
addr
.
nbytes
==
2
&&
op
->
cmd
.
buswidth
!=
op
->
addr
.
buswidth
&&
op
->
dummy
.
nbytes
==
0
)
return
false
;
#ifdef VERBOSE_DEBUG
/* If verbose debug is enabled, also dump the TX data */
if
(
cmd
->
enable
.
bits
.
data
&&
cmd
->
tx_buf
)
print_hex_dump
(
KERN_DEBUG
,
"qspi tx : "
,
DUMP_PREFIX_NONE
,
32
,
1
,
cmd
->
tx_buf
,
cmd
->
buf_len
,
false
);
#endif
return
true
;
}
#else
#define atmel_qspi_debug_command(aq, cmd, ifr)
#endif
static
int
atmel_qspi_run_command
(
struct
atmel_qspi
*
aq
,
const
struct
atmel_qspi_command
*
cmd
,
u32
ifr_tfrtyp
,
enum
spi_nor_protocol
proto
)
static
int
atmel_qspi_exec_op
(
struct
spi_mem
*
mem
,
const
struct
spi_mem_op
*
op
)
{
struct
atmel_qspi
*
aq
=
spi_controller_get_devdata
(
mem
->
spi
->
master
);
int
mode
;
u32
dummy_cycles
=
0
;
u32
iar
,
icr
,
ifr
,
sr
;
int
err
=
0
;
iar
=
0
;
icr
=
0
;
ifr
=
ifr_tfrtyp
;
/* Set the SPI protocol */
switch
(
proto
)
{
case
SNOR_PROTO_1_1_1
:
ifr
|=
QSPI_IFR_WIDTH_SINGLE_BIT_SPI
;
break
;
case
SNOR_PROTO_1_1_2
:
ifr
|=
QSPI_IFR_WIDTH_DUAL_OUTPUT
;
break
;
icr
=
QSPI_ICR_INST
(
op
->
cmd
.
opcode
);
ifr
=
QSPI_IFR_INSTEN
;
case
SNOR_PROTO_1_1_4
:
ifr
|=
QSPI_IFR_WIDTH_QUAD_OUTPUT
;
break
;
qspi_writel
(
aq
,
QSPI_MR
,
QSPI_MR_SMM
);
case
SNOR_PROTO_1_2_2
:
ifr
|=
QSPI_IFR_WIDTH_DUAL_IO
;
break
;
mode
=
find_mode
(
op
);
if
(
mode
<
0
)
return
-
ENOTSUPP
;
case
SNOR_PROTO_1_4_4
:
ifr
|=
QSPI_IFR_WIDTH_QUAD_IO
;
break
;
ifr
|=
sama5d2_qspi_modes
[
mode
].
config
;
case
SNOR_PROTO_2_2_2
:
ifr
|=
QSPI_IFR_WIDTH_DUAL_CMD
;
break
;
if
(
op
->
dummy
.
buswidth
&&
op
->
dummy
.
nbytes
)
dummy_cycles
=
op
->
dummy
.
nbytes
*
8
/
op
->
dummy
.
buswidth
;
case
SNOR_PROTO_4_4_4
:
ifr
|=
QSPI_IFR_WIDTH_QUAD_CMD
;
break
;
default:
return
-
EINVAL
;
}
/* Compute instruction parameters */
if
(
cmd
->
enable
.
bits
.
instruction
)
{
icr
|=
QSPI_ICR_INST
(
cmd
->
instruction
);
ifr
|=
QSPI_IFR_INSTEN
;
}
/* Compute address parameters */
switch
(
cmd
->
enable
.
bits
.
address
)
{
case
4
:
ifr
|=
QSPI_IFR_ADDRL
;
/* fall through to the 24bit (3 byte) address case. */
case
3
:
iar
=
(
cmd
->
enable
.
bits
.
data
)
?
0
:
cmd
->
address
;
ifr
|=
QSPI_IFR_ADDREN
;
break
;
if
(
op
->
addr
.
buswidth
)
{
switch
(
op
->
addr
.
nbytes
)
{
case
0
:
break
;
default:
return
-
EINVAL
;
}
/* Compute option parameters */
if
(
cmd
->
enable
.
bits
.
mode
&&
cmd
->
num_mode_cycles
)
{
u32
mode_cycle_bits
,
mode_bits
;
icr
|=
QSPI_ICR_OPT
(
cmd
->
mode
);
ifr
|=
QSPI_IFR_OPTEN
;
switch
(
ifr
&
QSPI_IFR_WIDTH_MASK
)
{
case
QSPI_IFR_WIDTH_SINGLE_BIT_SPI
:
case
QSPI_IFR_WIDTH_DUAL_OUTPUT
:
case
QSPI_IFR_WIDTH_QUAD_OUTPUT
:
mode_cycle_bits
=
1
;
break
;
case
QSPI_IFR_WIDTH_DUAL_IO
:
case
QSPI_IFR_WIDTH_DUAL_CMD
:
mode_cycle_bits
=
2
;
break
;
case
QSPI_IFR_WIDTH_QUAD_IO
:
case
QSPI_IFR_WIDTH_QUAD_CMD
:
mode_cycle_bits
=
4
;
break
;
default:
return
-
EINVAL
;
}
mode_bits
=
cmd
->
num_mode_cycles
*
mode_cycle_bits
;
switch
(
mode_bits
)
{
case
1
:
ifr
|=
QSPI_IFR_OPTL_1BIT
;
ifr
|=
QSPI_IFR_OPTEN
|
QSPI_IFR_OPTL_8BIT
;
icr
|=
QSPI_ICR_OPT
(
op
->
addr
.
val
&
0xff
);
break
;
case
2
:
ifr
|=
QSPI_IFR_OPTL_2BIT
;
if
(
dummy_cycles
<
8
/
op
->
addr
.
buswidth
)
{
ifr
&=
~
QSPI_IFR_INSTEN
;
ifr
|=
QSPI_IFR_ADDREN
;
iar
=
(
op
->
cmd
.
opcode
<<
16
)
|
(
op
->
addr
.
val
&
0xffff
);
}
else
{
ifr
|=
QSPI_IFR_ADDREN
;
iar
=
(
op
->
addr
.
val
<<
8
)
&
0xffffff
;
dummy_cycles
-=
8
/
op
->
addr
.
buswidth
;
}
break
;
case
4
:
i
fr
|=
QSPI_IFR_OPTL_4BIT
;
case
3
:
ifr
|=
QSPI_IFR_ADDREN
;
i
ar
=
op
->
addr
.
val
&
0xffffff
;
break
;
case
8
:
i
fr
|=
QSPI_IFR_OPTL_8BIT
;
case
4
:
ifr
|=
QSPI_IFR_ADDREN
|
QSPI_IFR_ADDRL
;
i
ar
=
op
->
addr
.
val
&
0x7ffffff
;
break
;
default:
return
-
E
INVAL
;
return
-
E
NOTSUPP
;
}
}
/* Set number of dummy cycles */
if
(
cmd
->
enable
.
bits
.
dummy
)
ifr
|=
QSPI_IFR_NBDUM
(
cmd
->
num_
dummy_cycles
);
if
(
dummy_cycles
)
ifr
|=
QSPI_IFR_NBDUM
(
dummy_cycles
);
/* Set data enable */
if
(
cmd
->
enable
.
bits
.
data
)
{
if
(
op
->
data
.
nbytes
)
ifr
|=
QSPI_IFR_DATAEN
;
/* Special case for Continuous Read Mode */
if
(
!
cmd
->
tx_buf
&&
!
cmd
->
rx_buf
)
ifr
|=
QSPI_IFR_CRM
;
}
if
(
op
->
data
.
dir
==
SPI_MEM_DATA_IN
&&
op
->
data
.
nbytes
)
if
r
|=
QSPI_IFR_TFRTYP_TRSFR_READ
;
else
ifr
|=
QSPI_IFR_TFRTYP_TRSFR_WRITE
;
/* Clear pending interrupts */
(
void
)
qspi_readl
(
aq
,
QSPI_SR
);
/* Set QSPI Instruction Frame registers */
atmel_qspi_debug_command
(
aq
,
cmd
,
ifr
);
qspi_writel
(
aq
,
QSPI_IAR
,
iar
);
qspi_writel
(
aq
,
QSPI_ICR
,
icr
);
qspi_writel
(
aq
,
QSPI_IFR
,
ifr
);
/* Skip to the final steps if there is no data */
if
(
!
cmd
->
enable
.
bits
.
data
)
goto
no_data
;
if
(
op
->
data
.
nbytes
)
{
/* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */
(
void
)
qspi_readl
(
aq
,
QSPI_IFR
);
/* Stop here for continuous read */
if
(
!
cmd
->
tx_buf
&&
!
cmd
->
rx_buf
)
return
0
;
/* Send/Receive data */
err
=
atmel_qspi_run_transfer
(
aq
,
cmd
);
if
(
op
->
data
.
dir
==
SPI_MEM_DATA_IN
)
_memcpy_fromio
(
op
->
data
.
buf
.
in
,
aq
->
mem
+
iar
,
op
->
data
.
nbytes
);
else
_memcpy_toio
(
aq
->
mem
+
iar
,
op
->
data
.
buf
.
out
,
op
->
data
.
nbytes
);
/* Release the chip-select */
qspi_writel
(
aq
,
QSPI_CR
,
QSPI_CR_LASTXFER
);
}
if
(
err
)
return
err
;
#if defined(DEBUG) && defined(VERBOSE_DEBUG)
/*
* If verbose debug is enabled, also dump the RX data in addition to
* the SPI command previously dumped by atmel_qspi_debug_command()
*/
if
(
cmd
->
rx_buf
)
print_hex_dump
(
KERN_DEBUG
,
"qspi rx : "
,
DUMP_PREFIX_NONE
,
32
,
1
,
cmd
->
rx_buf
,
cmd
->
buf_len
,
false
);
#endif
no_data:
/* Poll INSTRuction End status */
sr
=
qspi_readl
(
aq
,
QSPI_SR
);
if
((
sr
&
QSPI_SR_CMD_COMPLETED
)
==
QSPI_SR_CMD_COMPLETED
)
...
...
@@ -454,129 +337,50 @@ static int atmel_qspi_run_command(struct atmel_qspi *aq,
return
err
;
}
static
int
atmel_qspi_read_reg
(
struct
spi_nor
*
nor
,
u8
opcode
,
u8
*
buf
,
int
len
)
{
struct
atmel_qspi
*
aq
=
nor
->
priv
;
struct
atmel_qspi_command
cmd
;
memset
(
&
cmd
,
0
,
sizeof
(
cmd
));
cmd
.
enable
.
bits
.
instruction
=
1
;
cmd
.
enable
.
bits
.
data
=
1
;
cmd
.
instruction
=
opcode
;
cmd
.
rx_buf
=
buf
;
cmd
.
buf_len
=
len
;
return
atmel_qspi_run_command
(
aq
,
&
cmd
,
QSPI_IFR_TFRTYP_TRSFR_READ
,
nor
->
reg_proto
);
}
static
int
atmel_qspi_write_reg
(
struct
spi_nor
*
nor
,
u8
opcode
,
u8
*
buf
,
int
len
)
{
struct
atmel_qspi
*
aq
=
nor
->
priv
;
struct
atmel_qspi_command
cmd
;
memset
(
&
cmd
,
0
,
sizeof
(
cmd
));
cmd
.
enable
.
bits
.
instruction
=
1
;
cmd
.
enable
.
bits
.
data
=
(
buf
!=
NULL
&&
len
>
0
);
cmd
.
instruction
=
opcode
;
cmd
.
tx_buf
=
buf
;
cmd
.
buf_len
=
len
;
return
atmel_qspi_run_command
(
aq
,
&
cmd
,
QSPI_IFR_TFRTYP_TRSFR_WRITE
,
nor
->
reg_proto
);
}
static
ssize_t
atmel_qspi_write
(
struct
spi_nor
*
nor
,
loff_t
to
,
size_t
len
,
const
u_char
*
write_buf
)
{
struct
atmel_qspi
*
aq
=
nor
->
priv
;
struct
atmel_qspi_command
cmd
;
ssize_t
ret
;
memset
(
&
cmd
,
0
,
sizeof
(
cmd
));
cmd
.
enable
.
bits
.
instruction
=
1
;
cmd
.
enable
.
bits
.
address
=
nor
->
addr_width
;
cmd
.
enable
.
bits
.
data
=
1
;
cmd
.
instruction
=
nor
->
program_opcode
;
cmd
.
address
=
(
u32
)
to
;
cmd
.
tx_buf
=
write_buf
;
cmd
.
buf_len
=
len
;
ret
=
atmel_qspi_run_command
(
aq
,
&
cmd
,
QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM
,
nor
->
write_proto
);
return
(
ret
<
0
)
?
ret
:
len
;
}
static
int
atmel_qspi_erase
(
struct
spi_nor
*
nor
,
loff_t
offs
)
const
char
*
atmel_qspi_get_name
(
struct
spi_mem
*
spimem
)
{
struct
atmel_qspi
*
aq
=
nor
->
priv
;
struct
atmel_qspi_command
cmd
;
memset
(
&
cmd
,
0
,
sizeof
(
cmd
));
cmd
.
enable
.
bits
.
instruction
=
1
;
cmd
.
enable
.
bits
.
address
=
nor
->
addr_width
;
cmd
.
instruction
=
nor
->
erase_opcode
;
cmd
.
address
=
(
u32
)
offs
;
return
atmel_qspi_run_command
(
aq
,
&
cmd
,
QSPI_IFR_TFRTYP_TRSFR_WRITE
,
nor
->
reg_proto
);
return
dev_name
(
spimem
->
spi
->
dev
.
parent
);
}
static
ssize_t
atmel_qspi_read
(
struct
spi_nor
*
nor
,
loff_t
from
,
size_t
len
,
u_char
*
read_buf
)
{
struct
atmel_qspi
*
aq
=
nor
->
priv
;
struct
atmel_qspi_command
cmd
;
u8
num_mode_cycles
,
num_dummy_cycles
;
ssize_t
ret
;
if
(
nor
->
read_dummy
>=
2
)
{
num_mode_cycles
=
2
;
num_dummy_cycles
=
nor
->
read_dummy
-
2
;
}
else
{
num_mode_cycles
=
nor
->
read_dummy
;
num_dummy_cycles
=
0
;
}
memset
(
&
cmd
,
0
,
sizeof
(
cmd
));
cmd
.
enable
.
bits
.
instruction
=
1
;
cmd
.
enable
.
bits
.
address
=
nor
->
addr_width
;
cmd
.
enable
.
bits
.
mode
=
(
num_mode_cycles
>
0
);
cmd
.
enable
.
bits
.
dummy
=
(
num_dummy_cycles
>
0
);
cmd
.
enable
.
bits
.
data
=
1
;
cmd
.
instruction
=
nor
->
read_opcode
;
cmd
.
address
=
(
u32
)
from
;
cmd
.
mode
=
0xff
;
/* This value prevents from entering the 0-4-4 mode */
cmd
.
num_mode_cycles
=
num_mode_cycles
;
cmd
.
num_dummy_cycles
=
num_dummy_cycles
;
cmd
.
rx_buf
=
read_buf
;
cmd
.
buf_len
=
len
;
ret
=
atmel_qspi_run_command
(
aq
,
&
cmd
,
QSPI_IFR_TFRTYP_TRSFR_READ_MEM
,
nor
->
read_proto
);
return
(
ret
<
0
)
?
ret
:
len
;
}
static
const
struct
spi_controller_mem_ops
atmel_qspi_mem_ops
=
{
.
supports_op
=
atmel_qspi_supports_op
,
.
exec_op
=
atmel_qspi_exec_op
,
.
get_name
=
atmel_qspi_get_name
};
static
int
atmel_qspi_
init
(
struct
atmel_qspi
*
aq
)
static
int
atmel_qspi_
setup
(
struct
spi_device
*
spi
)
{
struct
spi_controller
*
ctrl
=
spi
->
master
;
struct
atmel_qspi
*
aq
=
spi_controller_get_devdata
(
ctrl
);
unsigned
long
src_rate
;
u32
mr
,
scr
,
scbr
;
u32
scr
,
scbr
;
/* Reset the QSPI controller */
qspi_writel
(
aq
,
QSPI_CR
,
QSPI_CR_SWRST
)
;
if
(
ctrl
->
busy
)
return
-
EBUSY
;
/* Set the QSPI controller in Serial Memory Mode */
mr
=
QSPI_MR_NBBITS
(
8
)
|
QSPI_MR_SSM
;
qspi_writel
(
aq
,
QSPI_MR
,
mr
);
if
(
!
spi
->
max_speed_hz
)
return
-
EINVAL
;
src_rate
=
clk_get_rate
(
aq
->
clk
);
if
(
!
src_rate
)
return
-
EINVAL
;
/* Compute the QSPI baudrate */
scbr
=
DIV_ROUND_UP
(
src_rate
,
aq
->
clk_rate
);
scbr
=
DIV_ROUND_UP
(
src_rate
,
spi
->
max_speed_hz
);
if
(
scbr
>
0
)
scbr
--
;
scr
=
QSPI_SCR_SCBR
(
scbr
);
qspi_writel
(
aq
,
QSPI_SCR
,
scr
);
return
0
;
}
static
int
atmel_qspi_init
(
struct
atmel_qspi
*
aq
)
{
/* Reset the QSPI controller */
qspi_writel
(
aq
,
QSPI_CR
,
QSPI_CR_SWRST
);
/* Enable the QSPI controller */
qspi_writel
(
aq
,
QSPI_CR
,
QSPI_CR_QSPIEN
);
...
...
@@ -604,38 +408,25 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
static
int
atmel_qspi_probe
(
struct
platform_device
*
pdev
)
{
const
struct
spi_nor_hwcaps
hwcaps
=
{
.
mask
=
SNOR_HWCAPS_READ
|
SNOR_HWCAPS_READ_FAST
|
SNOR_HWCAPS_READ_1_1_2
|
SNOR_HWCAPS_READ_1_2_2
|
SNOR_HWCAPS_READ_2_2_2
|
SNOR_HWCAPS_READ_1_1_4
|
SNOR_HWCAPS_READ_1_4_4
|
SNOR_HWCAPS_READ_4_4_4
|
SNOR_HWCAPS_PP
|
SNOR_HWCAPS_PP_1_1_4
|
SNOR_HWCAPS_PP_1_4_4
|
SNOR_HWCAPS_PP_4_4_4
,
};
struct
device_node
*
child
,
*
np
=
pdev
->
dev
.
of_node
;
struct
spi_controller
*
ctrl
;
struct
atmel_qspi
*
aq
;
struct
resource
*
res
;
struct
spi_nor
*
nor
;
struct
mtd_info
*
mtd
;
int
irq
,
err
=
0
;
if
(
of_get_child_count
(
np
)
!=
1
)
return
-
ENODEV
;
child
=
of_get_next_child
(
np
,
NULL
)
;
ctrl
=
spi_alloc_master
(
&
pdev
->
dev
,
sizeof
(
*
aq
));
if
(
!
ctrl
)
return
-
ENOMEM
;
aq
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
aq
),
GFP_KERNEL
);
if
(
!
aq
)
{
err
=
-
ENOMEM
;
goto
exit
;
}
ctrl
->
mode_bits
=
SPI_RX_DUAL
|
SPI_RX_QUAD
|
SPI_TX_DUAL
|
SPI_TX_QUAD
;
ctrl
->
setup
=
atmel_qspi_setup
;
ctrl
->
bus_num
=
-
1
;
ctrl
->
mem_ops
=
&
atmel_qspi_mem_ops
;
ctrl
->
num_chipselect
=
1
;
ctrl
->
dev
.
of_node
=
pdev
->
dev
.
of_node
;
platform_set_drvdata
(
pdev
,
ctrl
);
aq
=
spi_controller_get_devdata
(
ctrl
);
platform_set_drvdata
(
pdev
,
aq
);
init_completion
(
&
aq
->
cmd_completion
);
aq
->
pdev
=
pdev
;
...
...
@@ -684,54 +475,30 @@ static int atmel_qspi_probe(struct platform_device *pdev)
if
(
err
)
goto
disable_clk
;
/* Setup the spi-nor */
nor
=
&
aq
->
nor
;
mtd
=
&
nor
->
mtd
;
nor
->
dev
=
&
pdev
->
dev
;
spi_nor_set_flash_node
(
nor
,
child
);
nor
->
priv
=
aq
;
mtd
->
priv
=
nor
;
nor
->
read_reg
=
atmel_qspi_read_reg
;
nor
->
write_reg
=
atmel_qspi_write_reg
;
nor
->
read
=
atmel_qspi_read
;
nor
->
write
=
atmel_qspi_write
;
nor
->
erase
=
atmel_qspi_erase
;
err
=
of_property_read_u32
(
child
,
"spi-max-frequency"
,
&
aq
->
clk_rate
);
if
(
err
<
0
)
goto
disable_clk
;
err
=
atmel_qspi_init
(
aq
);
if
(
err
)
goto
disable_clk
;
err
=
spi_
nor_scan
(
nor
,
NULL
,
&
hwcaps
);
err
=
spi_
register_controller
(
ctrl
);
if
(
err
)
goto
disable_clk
;
err
=
mtd_device_register
(
mtd
,
NULL
,
0
);
if
(
err
)
goto
disable_clk
;
of_node_put
(
child
);
return
0
;
disable_clk:
clk_disable_unprepare
(
aq
->
clk
);
exit:
of_node_put
(
child
);
spi_controller_put
(
ctrl
);
return
err
;
}
static
int
atmel_qspi_remove
(
struct
platform_device
*
pdev
)
{
struct
atmel_qspi
*
aq
=
platform_get_drvdata
(
pdev
);
struct
spi_controller
*
ctrl
=
platform_get_drvdata
(
pdev
);
struct
atmel_qspi
*
aq
=
spi_controller_get_devdata
(
ctrl
);
mtd_device_unregister
(
&
aq
->
nor
.
mtd
);
spi_unregister_controller
(
ctrl
);
qspi_writel
(
aq
,
QSPI_CR
,
QSPI_CR_QSPIDIS
);
clk_disable_unprepare
(
aq
->
clk
);
return
0
;
...
...
@@ -777,5 +544,6 @@ static struct platform_driver atmel_qspi_driver = {
module_platform_driver
(
atmel_qspi_driver
);
MODULE_AUTHOR
(
"Cyrille Pitchen <cyrille.pitchen@atmel.com>"
);
MODULE_AUTHOR
(
"Piotr Bugalski <bugalski.piotr@gmail.com"
);
MODULE_DESCRIPTION
(
"Atmel QSPI Controller driver"
);
MODULE_LICENSE
(
"GPL v2"
);
drivers/spi/spi-mem.c
View file @
74ff666b
...
...
@@ -149,7 +149,7 @@ static bool spi_mem_default_supports_op(struct spi_mem *mem,
spi_check_buswidth_req
(
mem
,
op
->
dummy
.
buswidth
,
true
))
return
false
;
if
(
op
->
data
.
nbytes
&&
if
(
op
->
data
.
dir
!=
SPI_MEM_NO_DATA
&&
spi_check_buswidth_req
(
mem
,
op
->
data
.
buswidth
,
op
->
data
.
dir
==
SPI_MEM_DATA_OUT
))
return
false
;
...
...
@@ -220,6 +220,44 @@ bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
}
EXPORT_SYMBOL_GPL
(
spi_mem_supports_op
);
static
int
spi_mem_access_start
(
struct
spi_mem
*
mem
)
{
struct
spi_controller
*
ctlr
=
mem
->
spi
->
controller
;
/*
* Flush the message queue before executing our SPI memory
* operation to prevent preemption of regular SPI transfers.
*/
spi_flush_queue
(
ctlr
);
if
(
ctlr
->
auto_runtime_pm
)
{
int
ret
;
ret
=
pm_runtime_get_sync
(
ctlr
->
dev
.
parent
);
if
(
ret
<
0
)
{
dev_err
(
&
ctlr
->
dev
,
"Failed to power device: %d
\n
"
,
ret
);
return
ret
;
}
}
mutex_lock
(
&
ctlr
->
bus_lock_mutex
);
mutex_lock
(
&
ctlr
->
io_mutex
);
return
0
;
}
static
void
spi_mem_access_end
(
struct
spi_mem
*
mem
)
{
struct
spi_controller
*
ctlr
=
mem
->
spi
->
controller
;
mutex_unlock
(
&
ctlr
->
io_mutex
);
mutex_unlock
(
&
ctlr
->
bus_lock_mutex
);
if
(
ctlr
->
auto_runtime_pm
)
pm_runtime_put
(
ctlr
->
dev
.
parent
);
}
/**
* spi_mem_exec_op() - Execute a memory operation
* @mem: the SPI memory
...
...
@@ -249,30 +287,13 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
return
-
ENOTSUPP
;
if
(
ctlr
->
mem_ops
)
{
/*
* Flush the message queue before executing our SPI memory
* operation to prevent preemption of regular SPI transfers.
*/
spi_flush_queue
(
ctlr
);
if
(
ctlr
->
auto_runtime_pm
)
{
ret
=
pm_runtime_get_sync
(
ctlr
->
dev
.
parent
);
if
(
ret
<
0
)
{
dev_err
(
&
ctlr
->
dev
,
"Failed to power device: %d
\n
"
,
ret
);
ret
=
spi_mem_access_start
(
mem
);
if
(
ret
)
return
ret
;
}
}
mutex_lock
(
&
ctlr
->
bus_lock_mutex
);
mutex_lock
(
&
ctlr
->
io_mutex
);
ret
=
ctlr
->
mem_ops
->
exec_op
(
mem
,
op
);
mutex_unlock
(
&
ctlr
->
io_mutex
);
mutex_unlock
(
&
ctlr
->
bus_lock_mutex
);
if
(
ctlr
->
auto_runtime_pm
)
pm_runtime_put
(
ctlr
->
dev
.
parent
);
spi_mem_access_end
(
mem
);
/*
* Some controllers only optimize specific paths (typically the
...
...
@@ -418,6 +439,210 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
}
EXPORT_SYMBOL_GPL
(
spi_mem_adjust_op_size
);
static
ssize_t
spi_mem_no_dirmap_read
(
struct
spi_mem_dirmap_desc
*
desc
,
u64
offs
,
size_t
len
,
void
*
buf
)
{
struct
spi_mem_op
op
=
desc
->
info
.
op_tmpl
;
int
ret
;
op
.
addr
.
val
=
desc
->
info
.
offset
+
offs
;
op
.
data
.
buf
.
in
=
buf
;
op
.
data
.
nbytes
=
len
;
ret
=
spi_mem_adjust_op_size
(
desc
->
mem
,
&
op
);
if
(
ret
)
return
ret
;
ret
=
spi_mem_exec_op
(
desc
->
mem
,
&
op
);
if
(
ret
)
return
ret
;
return
op
.
data
.
nbytes
;
}
static
ssize_t
spi_mem_no_dirmap_write
(
struct
spi_mem_dirmap_desc
*
desc
,
u64
offs
,
size_t
len
,
const
void
*
buf
)
{
struct
spi_mem_op
op
=
desc
->
info
.
op_tmpl
;
int
ret
;
op
.
addr
.
val
=
desc
->
info
.
offset
+
offs
;
op
.
data
.
buf
.
out
=
buf
;
op
.
data
.
nbytes
=
len
;
ret
=
spi_mem_adjust_op_size
(
desc
->
mem
,
&
op
);
if
(
ret
)
return
ret
;
ret
=
spi_mem_exec_op
(
desc
->
mem
,
&
op
);
if
(
ret
)
return
ret
;
return
op
.
data
.
nbytes
;
}
/**
* spi_mem_dirmap_create() - Create a direct mapping descriptor
* @mem: SPI mem device this direct mapping should be created for
* @info: direct mapping information
*
* This function is creating a direct mapping descriptor which can then be used
* to access the memory using spi_mem_dirmap_read() or spi_mem_dirmap_write().
* If the SPI controller driver does not support direct mapping, this function
* fallback to an implementation using spi_mem_exec_op(), so that the caller
* doesn't have to bother implementing a fallback on his own.
*
* Return: a valid pointer in case of success, and ERR_PTR() otherwise.
*/
struct
spi_mem_dirmap_desc
*
spi_mem_dirmap_create
(
struct
spi_mem
*
mem
,
const
struct
spi_mem_dirmap_info
*
info
)
{
struct
spi_controller
*
ctlr
=
mem
->
spi
->
controller
;
struct
spi_mem_dirmap_desc
*
desc
;
int
ret
=
-
ENOTSUPP
;
/* Make sure the number of address cycles is between 1 and 8 bytes. */
if
(
!
info
->
op_tmpl
.
addr
.
nbytes
||
info
->
op_tmpl
.
addr
.
nbytes
>
8
)
return
ERR_PTR
(
-
EINVAL
);
/* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */
if
(
info
->
op_tmpl
.
data
.
dir
==
SPI_MEM_NO_DATA
)
return
ERR_PTR
(
-
EINVAL
);
desc
=
kzalloc
(
sizeof
(
*
desc
),
GFP_KERNEL
);
if
(
!
desc
)
return
ERR_PTR
(
-
ENOMEM
);
desc
->
mem
=
mem
;
desc
->
info
=
*
info
;
if
(
ctlr
->
mem_ops
&&
ctlr
->
mem_ops
->
dirmap_create
)
ret
=
ctlr
->
mem_ops
->
dirmap_create
(
desc
);
if
(
ret
)
{
desc
->
nodirmap
=
true
;
if
(
!
spi_mem_supports_op
(
desc
->
mem
,
&
desc
->
info
.
op_tmpl
))
ret
=
-
ENOTSUPP
;
else
ret
=
0
;
}
if
(
ret
)
{
kfree
(
desc
);
return
ERR_PTR
(
ret
);
}
return
desc
;
}
EXPORT_SYMBOL_GPL
(
spi_mem_dirmap_create
);
/**
* spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor
* @desc: the direct mapping descriptor to destroy
* @info: direct mapping information
*
* This function destroys a direct mapping descriptor previously created by
* spi_mem_dirmap_create().
*/
void
spi_mem_dirmap_destroy
(
struct
spi_mem_dirmap_desc
*
desc
)
{
struct
spi_controller
*
ctlr
=
desc
->
mem
->
spi
->
controller
;
if
(
!
desc
->
nodirmap
&&
ctlr
->
mem_ops
&&
ctlr
->
mem_ops
->
dirmap_destroy
)
ctlr
->
mem_ops
->
dirmap_destroy
(
desc
);
}
EXPORT_SYMBOL_GPL
(
spi_mem_dirmap_destroy
);
/**
* spi_mem_dirmap_dirmap_read() - Read data through a direct mapping
* @desc: direct mapping descriptor
* @offs: offset to start reading from. Note that this is not an absolute
* offset, but the offset within the direct mapping which already has
* its own offset
* @len: length in bytes
* @buf: destination buffer. This buffer must be DMA-able
*
* This function reads data from a memory device using a direct mapping
* previously instantiated with spi_mem_dirmap_create().
*
* Return: the amount of data read from the memory device or a negative error
* code. Note that the returned size might be smaller than @len, and the caller
* is responsible for calling spi_mem_dirmap_read() again when that happens.
*/
ssize_t
spi_mem_dirmap_read
(
struct
spi_mem_dirmap_desc
*
desc
,
u64
offs
,
size_t
len
,
void
*
buf
)
{
struct
spi_controller
*
ctlr
=
desc
->
mem
->
spi
->
controller
;
ssize_t
ret
;
if
(
desc
->
info
.
op_tmpl
.
data
.
dir
!=
SPI_MEM_DATA_IN
)
return
-
EINVAL
;
if
(
!
len
)
return
0
;
if
(
desc
->
nodirmap
)
{
ret
=
spi_mem_no_dirmap_read
(
desc
,
offs
,
len
,
buf
);
}
else
if
(
ctlr
->
mem_ops
&&
ctlr
->
mem_ops
->
dirmap_read
)
{
ret
=
spi_mem_access_start
(
desc
->
mem
);
if
(
ret
)
return
ret
;
ret
=
ctlr
->
mem_ops
->
dirmap_read
(
desc
,
offs
,
len
,
buf
);
spi_mem_access_end
(
desc
->
mem
);
}
else
{
ret
=
-
ENOTSUPP
;
}
return
ret
;
}
EXPORT_SYMBOL_GPL
(
spi_mem_dirmap_read
);
/**
* spi_mem_dirmap_dirmap_write() - Write data through a direct mapping
* @desc: direct mapping descriptor
* @offs: offset to start writing from. Note that this is not an absolute
* offset, but the offset within the direct mapping which already has
* its own offset
* @len: length in bytes
* @buf: source buffer. This buffer must be DMA-able
*
* This function writes data to a memory device using a direct mapping
* previously instantiated with spi_mem_dirmap_create().
*
* Return: the amount of data written to the memory device or a negative error
* code. Note that the returned size might be smaller than @len, and the caller
* is responsible for calling spi_mem_dirmap_write() again when that happens.
*/
ssize_t
spi_mem_dirmap_write
(
struct
spi_mem_dirmap_desc
*
desc
,
u64
offs
,
size_t
len
,
const
void
*
buf
)
{
struct
spi_controller
*
ctlr
=
desc
->
mem
->
spi
->
controller
;
ssize_t
ret
;
if
(
desc
->
info
.
op_tmpl
.
data
.
dir
!=
SPI_MEM_DATA_OUT
)
return
-
EINVAL
;
if
(
!
len
)
return
0
;
if
(
desc
->
nodirmap
)
{
ret
=
spi_mem_no_dirmap_write
(
desc
,
offs
,
len
,
buf
);
}
else
if
(
ctlr
->
mem_ops
&&
ctlr
->
mem_ops
->
dirmap_write
)
{
ret
=
spi_mem_access_start
(
desc
->
mem
);
if
(
ret
)
return
ret
;
ret
=
ctlr
->
mem_ops
->
dirmap_write
(
desc
,
offs
,
len
,
buf
);
spi_mem_access_end
(
desc
->
mem
);
}
else
{
ret
=
-
ENOTSUPP
;
}
return
ret
;
}
EXPORT_SYMBOL_GPL
(
spi_mem_dirmap_write
);
static
inline
struct
spi_mem_driver
*
to_spi_mem_drv
(
struct
device_driver
*
drv
)
{
return
container_of
(
drv
,
struct
spi_mem_driver
,
spidrv
.
driver
);
...
...
include/linux/spi/spi-mem.h
View file @
74ff666b
...
...
@@ -57,10 +57,12 @@
/**
* enum spi_mem_data_dir - describes the direction of a SPI memory data
* transfer from the controller perspective
* @SPI_MEM_NO_DATA: no data transferred
* @SPI_MEM_DATA_IN: data coming from the SPI memory
* @SPI_MEM_DATA_OUT: data sent the SPI memory
* @SPI_MEM_DATA_OUT: data sent t
o t
he SPI memory
*/
enum
spi_mem_data_dir
{
SPI_MEM_NO_DATA
,
SPI_MEM_DATA_IN
,
SPI_MEM_DATA_OUT
,
};
...
...
@@ -122,6 +124,49 @@ struct spi_mem_op {
.data = __data, \
}
/**
* struct spi_mem_dirmap_info - Direct mapping information
* @op_tmpl: operation template that should be used by the direct mapping when
* the memory device is accessed
* @offset: absolute offset this direct mapping is pointing to
* @length: length in byte of this direct mapping
*
* These information are used by the controller specific implementation to know
* the portion of memory that is directly mapped and the spi_mem_op that should
* be used to access the device.
* A direct mapping is only valid for one direction (read or write) and this
* direction is directly encoded in the ->op_tmpl.data.dir field.
*/
struct
spi_mem_dirmap_info
{
struct
spi_mem_op
op_tmpl
;
u64
offset
;
u64
length
;
};
/**
* struct spi_mem_dirmap_desc - Direct mapping descriptor
* @mem: the SPI memory device this direct mapping is attached to
* @info: information passed at direct mapping creation time
* @nodirmap: set to 1 if the SPI controller does not implement
* ->mem_ops->dirmap_create() or when this function returned an
* error. If @nodirmap is true, all spi_mem_dirmap_{read,write}()
* calls will use spi_mem_exec_op() to access the memory. This is a
* degraded mode that allows spi_mem drivers to use the same code
* no matter whether the controller supports direct mapping or not
* @priv: field pointing to controller specific data
*
* Common part of a direct mapping descriptor. This object is created by
* spi_mem_dirmap_create() and controller implementation of ->create_dirmap()
* can create/attach direct mapping resources to the descriptor in the ->priv
* field.
*/
struct
spi_mem_dirmap_desc
{
struct
spi_mem
*
mem
;
struct
spi_mem_dirmap_info
info
;
unsigned
int
nodirmap
;
void
*
priv
;
};
/**
* struct spi_mem - describes a SPI memory device
* @spi: the underlying SPI device
...
...
@@ -177,10 +222,32 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
* Note that if the implementation of this function allocates memory
* dynamically, then it should do so with devm_xxx(), as we don't
* have a ->free_name() function.
* @dirmap_create: create a direct mapping descriptor that can later be used to
* access the memory device. This method is optional
* @dirmap_destroy: destroy a memory descriptor previous created by
* ->dirmap_create()
* @dirmap_read: read data from the memory device using the direct mapping
* created by ->dirmap_create(). The function can return less
* data than requested (for example when the request is crossing
* the currently mapped area), and the caller of
* spi_mem_dirmap_read() is responsible for calling it again in
* this case.
* @dirmap_write: write data to the memory device using the direct mapping
* created by ->dirmap_create(). The function can return less
* data than requested (for example when the request is crossing
* the currently mapped area), and the caller of
* spi_mem_dirmap_write() is responsible for calling it again in
* this case.
*
* This interface should be implemented by SPI controllers providing an
* high-level interface to execute SPI memory operation, which is usually the
* case for QSPI controllers.
*
* Note on ->dirmap_{read,write}(): drivers should avoid accessing the direct
* mapping from the CPU because doing that can stall the CPU waiting for the
* SPI mem transaction to finish, and this will make real-time maintainers
* unhappy and might make your system less reactive. Instead, drivers should
* use DMA to access this direct mapping.
*/
struct
spi_controller_mem_ops
{
int
(
*
adjust_op_size
)(
struct
spi_mem
*
mem
,
struct
spi_mem_op
*
op
);
...
...
@@ -189,6 +256,12 @@ struct spi_controller_mem_ops {
int
(
*
exec_op
)(
struct
spi_mem
*
mem
,
const
struct
spi_mem_op
*
op
);
const
char
*
(
*
get_name
)(
struct
spi_mem
*
mem
);
int
(
*
dirmap_create
)(
struct
spi_mem_dirmap_desc
*
desc
);
void
(
*
dirmap_destroy
)(
struct
spi_mem_dirmap_desc
*
desc
);
ssize_t
(
*
dirmap_read
)(
struct
spi_mem_dirmap_desc
*
desc
,
u64
offs
,
size_t
len
,
void
*
buf
);
ssize_t
(
*
dirmap_write
)(
struct
spi_mem_dirmap_desc
*
desc
,
u64
offs
,
size_t
len
,
const
void
*
buf
);
};
/**
...
...
@@ -249,6 +322,15 @@ int spi_mem_exec_op(struct spi_mem *mem,
const
char
*
spi_mem_get_name
(
struct
spi_mem
*
mem
);
struct
spi_mem_dirmap_desc
*
spi_mem_dirmap_create
(
struct
spi_mem
*
mem
,
const
struct
spi_mem_dirmap_info
*
info
);
void
spi_mem_dirmap_destroy
(
struct
spi_mem_dirmap_desc
*
desc
);
ssize_t
spi_mem_dirmap_read
(
struct
spi_mem_dirmap_desc
*
desc
,
u64
offs
,
size_t
len
,
void
*
buf
);
ssize_t
spi_mem_dirmap_write
(
struct
spi_mem_dirmap_desc
*
desc
,
u64
offs
,
size_t
len
,
const
void
*
buf
);
int
spi_mem_driver_register_with_owner
(
struct
spi_mem_driver
*
drv
,
struct
module
*
owner
);
...
...
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