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