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
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