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
Expand all
Hide 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
This diff is collapsed.
Click to expand it.
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
);
return
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