mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-01 22:54:01 +00:00
mtd: nand: ecc: Add infrastructure to support hardware engines
Add the necessary helpers to register/unregister hardware ECC engines that will be called from ECC engine drivers. Also add helpers to get the right engine from the user perspective. Keep a reference of the in use ECC engine in order to prevent modules to be unloaded. Put the reference when the engine gets retired. A static list of hardware (only) ECC engines is setup to keep track of the registered engines. Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Link: https://lore.kernel.org/linux-mtd/20211216111654.238086-13-miquel.raynal@bootlin.com
This commit is contained in:
parent
1d46963d45
commit
96489c1c0b
3 changed files with 123 additions and 3 deletions
|
@ -235,7 +235,9 @@ static int nanddev_get_ecc_engine(struct nand_device *nand)
|
||||||
nand->ecc.engine = nand_ecc_get_on_die_hw_engine(nand);
|
nand->ecc.engine = nand_ecc_get_on_die_hw_engine(nand);
|
||||||
break;
|
break;
|
||||||
case NAND_ECC_ENGINE_TYPE_ON_HOST:
|
case NAND_ECC_ENGINE_TYPE_ON_HOST:
|
||||||
pr_err("On-host hardware ECC engines not supported yet\n");
|
nand->ecc.engine = nand_ecc_get_on_host_hw_engine(nand);
|
||||||
|
if (PTR_ERR(nand->ecc.engine) == -EPROBE_DEFER)
|
||||||
|
return -EPROBE_DEFER;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pr_err("Missing ECC engine type\n");
|
pr_err("Missing ECC engine type\n");
|
||||||
|
@ -255,7 +257,7 @@ static int nanddev_put_ecc_engine(struct nand_device *nand)
|
||||||
{
|
{
|
||||||
switch (nand->ecc.ctx.conf.engine_type) {
|
switch (nand->ecc.ctx.conf.engine_type) {
|
||||||
case NAND_ECC_ENGINE_TYPE_ON_HOST:
|
case NAND_ECC_ENGINE_TYPE_ON_HOST:
|
||||||
pr_err("On-host hardware ECC engines not supported yet\n");
|
nand_ecc_put_on_host_hw_engine(nand);
|
||||||
break;
|
break;
|
||||||
case NAND_ECC_ENGINE_TYPE_NONE:
|
case NAND_ECC_ENGINE_TYPE_NONE:
|
||||||
case NAND_ECC_ENGINE_TYPE_SOFT:
|
case NAND_ECC_ENGINE_TYPE_SOFT:
|
||||||
|
@ -300,7 +302,9 @@ int nanddev_ecc_engine_init(struct nand_device *nand)
|
||||||
/* Look for the ECC engine to use */
|
/* Look for the ECC engine to use */
|
||||||
ret = nanddev_get_ecc_engine(nand);
|
ret = nanddev_get_ecc_engine(nand);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("No ECC engine found\n");
|
if (ret != -EPROBE_DEFER)
|
||||||
|
pr_err("No ECC engine found\n");
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,6 +96,12 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/mtd/nand.h>
|
#include <linux/mtd/nand.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
|
|
||||||
|
static LIST_HEAD(on_host_hw_engines);
|
||||||
|
static DEFINE_MUTEX(on_host_hw_engines_mutex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_ecc_init_ctx - Init the ECC engine context
|
* nand_ecc_init_ctx - Init the ECC engine context
|
||||||
|
@ -611,6 +617,88 @@ struct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(nand_ecc_get_on_die_hw_engine);
|
EXPORT_SYMBOL(nand_ecc_get_on_die_hw_engine);
|
||||||
|
|
||||||
|
int nand_ecc_register_on_host_hw_engine(struct nand_ecc_engine *engine)
|
||||||
|
{
|
||||||
|
struct nand_ecc_engine *item;
|
||||||
|
|
||||||
|
if (!engine)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Prevent multiple registrations of one engine */
|
||||||
|
list_for_each_entry(item, &on_host_hw_engines, node)
|
||||||
|
if (item == engine)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
mutex_lock(&on_host_hw_engines_mutex);
|
||||||
|
list_add_tail(&engine->node, &on_host_hw_engines);
|
||||||
|
mutex_unlock(&on_host_hw_engines_mutex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(nand_ecc_register_on_host_hw_engine);
|
||||||
|
|
||||||
|
int nand_ecc_unregister_on_host_hw_engine(struct nand_ecc_engine *engine)
|
||||||
|
{
|
||||||
|
if (!engine)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mutex_lock(&on_host_hw_engines_mutex);
|
||||||
|
list_del(&engine->node);
|
||||||
|
mutex_unlock(&on_host_hw_engines_mutex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(nand_ecc_unregister_on_host_hw_engine);
|
||||||
|
|
||||||
|
static struct nand_ecc_engine *nand_ecc_match_on_host_hw_engine(struct device *dev)
|
||||||
|
{
|
||||||
|
struct nand_ecc_engine *item;
|
||||||
|
|
||||||
|
list_for_each_entry(item, &on_host_hw_engines, node)
|
||||||
|
if (item->dev == dev)
|
||||||
|
return item;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nand_ecc_engine *nand_ecc_get_on_host_hw_engine(struct nand_device *nand)
|
||||||
|
{
|
||||||
|
struct nand_ecc_engine *engine = NULL;
|
||||||
|
struct device *dev = &nand->mtd.dev;
|
||||||
|
struct platform_device *pdev;
|
||||||
|
struct device_node *np;
|
||||||
|
|
||||||
|
if (list_empty(&on_host_hw_engines))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Check for an explicit nand-ecc-engine property */
|
||||||
|
np = of_parse_phandle(dev->of_node, "nand-ecc-engine", 0);
|
||||||
|
if (np) {
|
||||||
|
pdev = of_find_device_by_node(np);
|
||||||
|
if (!pdev)
|
||||||
|
return ERR_PTR(-EPROBE_DEFER);
|
||||||
|
|
||||||
|
engine = nand_ecc_match_on_host_hw_engine(&pdev->dev);
|
||||||
|
platform_device_put(pdev);
|
||||||
|
of_node_put(np);
|
||||||
|
|
||||||
|
if (!engine)
|
||||||
|
return ERR_PTR(-EPROBE_DEFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (engine)
|
||||||
|
get_device(engine->dev);
|
||||||
|
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(nand_ecc_get_on_host_hw_engine);
|
||||||
|
|
||||||
|
void nand_ecc_put_on_host_hw_engine(struct nand_device *nand)
|
||||||
|
{
|
||||||
|
put_device(nand->ecc.engine->dev);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(nand_ecc_put_on_host_hw_engine);
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
|
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
|
||||||
MODULE_DESCRIPTION("Generic ECC engine");
|
MODULE_DESCRIPTION("Generic ECC engine");
|
||||||
|
|
|
@ -263,12 +263,36 @@ struct nand_ecc_engine_ops {
|
||||||
struct nand_page_io_req *req);
|
struct nand_page_io_req *req);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum nand_ecc_engine_integration - How the NAND ECC engine is integrated
|
||||||
|
* @NAND_ECC_ENGINE_INTEGRATION_INVALID: Invalid value
|
||||||
|
* @NAND_ECC_ENGINE_INTEGRATION_PIPELINED: Pipelined engine, performs on-the-fly
|
||||||
|
* correction, does not need to copy
|
||||||
|
* data around
|
||||||
|
* @NAND_ECC_ENGINE_INTEGRATION_EXTERNAL: External engine, needs to bring the
|
||||||
|
* data into its own area before use
|
||||||
|
*/
|
||||||
|
enum nand_ecc_engine_integration {
|
||||||
|
NAND_ECC_ENGINE_INTEGRATION_INVALID,
|
||||||
|
NAND_ECC_ENGINE_INTEGRATION_PIPELINED,
|
||||||
|
NAND_ECC_ENGINE_INTEGRATION_EXTERNAL,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct nand_ecc_engine - ECC engine abstraction for NAND devices
|
* struct nand_ecc_engine - ECC engine abstraction for NAND devices
|
||||||
|
* @dev: Host device
|
||||||
|
* @node: Private field for registration time
|
||||||
* @ops: ECC engine operations
|
* @ops: ECC engine operations
|
||||||
|
* @integration: How the engine is integrated with the host
|
||||||
|
* (only relevant on %NAND_ECC_ENGINE_TYPE_ON_HOST engines)
|
||||||
|
* @priv: Private data
|
||||||
*/
|
*/
|
||||||
struct nand_ecc_engine {
|
struct nand_ecc_engine {
|
||||||
|
struct device *dev;
|
||||||
|
struct list_head node;
|
||||||
struct nand_ecc_engine_ops *ops;
|
struct nand_ecc_engine_ops *ops;
|
||||||
|
enum nand_ecc_engine_integration integration;
|
||||||
|
void *priv;
|
||||||
};
|
};
|
||||||
|
|
||||||
void of_get_nand_ecc_user_config(struct nand_device *nand);
|
void of_get_nand_ecc_user_config(struct nand_device *nand);
|
||||||
|
@ -279,8 +303,12 @@ int nand_ecc_prepare_io_req(struct nand_device *nand,
|
||||||
int nand_ecc_finish_io_req(struct nand_device *nand,
|
int nand_ecc_finish_io_req(struct nand_device *nand,
|
||||||
struct nand_page_io_req *req);
|
struct nand_page_io_req *req);
|
||||||
bool nand_ecc_is_strong_enough(struct nand_device *nand);
|
bool nand_ecc_is_strong_enough(struct nand_device *nand);
|
||||||
|
int nand_ecc_register_on_host_hw_engine(struct nand_ecc_engine *engine);
|
||||||
|
int nand_ecc_unregister_on_host_hw_engine(struct nand_ecc_engine *engine);
|
||||||
struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand);
|
struct nand_ecc_engine *nand_ecc_get_sw_engine(struct nand_device *nand);
|
||||||
struct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand);
|
struct nand_ecc_engine *nand_ecc_get_on_die_hw_engine(struct nand_device *nand);
|
||||||
|
struct nand_ecc_engine *nand_ecc_get_on_host_hw_engine(struct nand_device *nand);
|
||||||
|
void nand_ecc_put_on_host_hw_engine(struct nand_device *nand);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING)
|
#if IS_ENABLED(CONFIG_MTD_NAND_ECC_SW_HAMMING)
|
||||||
struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void);
|
struct nand_ecc_engine *nand_ecc_sw_hamming_get_engine(void);
|
||||||
|
|
Loading…
Reference in a new issue