mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 03:01:39 -04:00
Char/Misc patches for 4.15-rc1
Here is the big set of char/misc and other driver subsystem patches for 4.15-rc1. There are small changes all over here, hyperv driver updates, pcmcia driver updates, w1 driver updats, vme driver updates, nvmem driver updates, and lots of other little one-off driver updates as well. The shortlog has the full details. Note, there will be a merge conflict in drivers/misc/lkdtm_core.c when merging to your tree as one lkdtm patch came in through the perf tree as well as this one. The resolution is to take the const change that this tree provides. All of these have been in linux-next for quite a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <[email protected]> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWg2Lnw8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ymTUwCgwp46+I8yPlgDH8oe5TxyyJnpdHQAn1XW0i+a sBi6WS87In5v1QO1Rgfc =dH2a -----END PGP SIGNATURE----- Merge tag 'char-misc-4.15-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char/misc updates from Greg KH: "Here is the big set of char/misc and other driver subsystem patches for 4.15-rc1. There are small changes all over here, hyperv driver updates, pcmcia driver updates, w1 driver updats, vme driver updates, nvmem driver updates, and lots of other little one-off driver updates as well. The shortlog has the full details. All of these have been in linux-next for quite a while with no reported issues" * tag 'char-misc-4.15-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (90 commits) VME: Return -EBUSY when DMA list in use w1: keep balance of mutex locks and refcnts MAINTAINERS: Update VME subsystem tree. nvmem: sunxi-sid: add support for A64/H5's SID controller nvmem: imx-ocotp: Update module description nvmem: imx-ocotp: Enable i.MX7D OTP write support nvmem: imx-ocotp: Add i.MX7D timing write clock setup support nvmem: imx-ocotp: Move i.MX6 write clock setup to dedicated function nvmem: imx-ocotp: Add support for banked OTP addressing nvmem: imx-ocotp: Pass parameters via a struct nvmem: imx-ocotp: Restrict OTP write to IMX6 processors nvmem: uniphier: add UniPhier eFuse driver dt-bindings: nvmem: add description for UniPhier eFuse nvmem: set nvmem->owner to nvmem->dev->driver->owner if unset nvmem: qfprom: fix different address space warnings of sparse nvmem: mtk-efuse: fix different address space warnings of sparse nvmem: mtk-efuse: use stack for nvmem_config instead of malloc'ing it nvmem: imx-iim: use stack for nvmem_config instead of malloc'ing it thunderbolt: tb: fix use after free in tb_activate_pcie_devices MAINTAINERS: Add git tree for Thunderbolt development ...
This commit is contained in:
commit
2bf16b7a73
79 changed files with 2770 additions and 517 deletions
|
|
@ -123,6 +123,17 @@ config NVMEM_SUNXI_SID
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called nvmem_sunxi_sid.
|
||||
|
||||
config UNIPHIER_EFUSE
|
||||
tristate "UniPhier SoCs eFuse support"
|
||||
depends on ARCH_UNIPHIER || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
This is a simple driver to dump specified values of UniPhier SoC
|
||||
from eFuse.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called nvmem-uniphier-efuse.
|
||||
|
||||
config NVMEM_VF610_OCOTP
|
||||
tristate "VF610 SoC OCOTP support"
|
||||
depends on SOC_VF610 || COMPILE_TEST
|
||||
|
|
@ -135,13 +146,33 @@ config NVMEM_VF610_OCOTP
|
|||
be called nvmem-vf610-ocotp.
|
||||
|
||||
config MESON_EFUSE
|
||||
tristate "Amlogic eFuse Support"
|
||||
tristate "Amlogic Meson GX eFuse Support"
|
||||
depends on (ARCH_MESON || COMPILE_TEST) && MESON_SM
|
||||
help
|
||||
This is a driver to retrieve specific values from the eFuse found on
|
||||
the Amlogic Meson SoCs.
|
||||
the Amlogic Meson GX SoCs.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called nvmem_meson_efuse.
|
||||
|
||||
config MESON_MX_EFUSE
|
||||
tristate "Amlogic Meson6/Meson8/Meson8b eFuse Support"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
help
|
||||
This is a driver to retrieve specific values from the eFuse found on
|
||||
the Amlogic Meson6, Meson8 and Meson8b SoCs.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called nvmem_meson_mx_efuse.
|
||||
|
||||
config NVMEM_SNVS_LPGPR
|
||||
tristate "Support for Low Power General Purpose Register"
|
||||
depends on SOC_IMX6 || COMPILE_TEST
|
||||
help
|
||||
This is a driver for Low Power General Purpose Register (LPGPR) available on
|
||||
i.MX6 SoCs in Secure Non-Volatile Storage (SNVS) of this chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called nvmem-snvs-lpgpr.
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -27,7 +27,13 @@ obj-$(CONFIG_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o
|
|||
nvmem_rockchip_efuse-y := rockchip-efuse.o
|
||||
obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o
|
||||
nvmem_sunxi_sid-y := sunxi_sid.o
|
||||
obj-$(CONFIG_UNIPHIER_EFUSE) += nvmem-uniphier-efuse.o
|
||||
nvmem-uniphier-efuse-y := uniphier-efuse.o
|
||||
obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o
|
||||
nvmem-vf610-ocotp-y := vf610-ocotp.o
|
||||
obj-$(CONFIG_MESON_EFUSE) += nvmem_meson_efuse.o
|
||||
nvmem_meson_efuse-y := meson-efuse.o
|
||||
obj-$(CONFIG_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o
|
||||
nvmem_meson_mx_efuse-y := meson-mx-efuse.o
|
||||
obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o
|
||||
nvmem_snvs_lpgpr-y := snvs_lpgpr.o
|
||||
|
|
|
|||
|
|
@ -232,7 +232,6 @@ static struct nvmem_config bcm_otpc_nvmem_config = {
|
|||
.read_only = false,
|
||||
.word_size = 4,
|
||||
.stride = 4,
|
||||
.owner = THIS_MODULE,
|
||||
.reg_read = bcm_otpc_read,
|
||||
.reg_write = bcm_otpc_write,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -462,6 +462,8 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
|
|||
|
||||
nvmem->id = rval;
|
||||
nvmem->owner = config->owner;
|
||||
if (!nvmem->owner && config->dev->driver)
|
||||
nvmem->owner = config->dev->driver->owner;
|
||||
nvmem->stride = config->stride;
|
||||
nvmem->word_size = config->word_size;
|
||||
nvmem->size = config->size;
|
||||
|
|
@ -615,7 +617,7 @@ static struct nvmem_device *nvmem_find(const char *name)
|
|||
return to_nvmem_device(d);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)
|
||||
#if IS_ENABLED(CONFIG_OF)
|
||||
/**
|
||||
* of_nvmem_device_get() - Get nvmem device from a given id
|
||||
*
|
||||
|
|
@ -753,7 +755,7 @@ static struct nvmem_cell *nvmem_cell_get_from_list(const char *cell_id)
|
|||
return cell;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF)
|
||||
#if IS_ENABLED(CONFIG_OF)
|
||||
/**
|
||||
* of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id
|
||||
*
|
||||
|
|
@ -946,8 +948,7 @@ void nvmem_cell_put(struct nvmem_cell *cell)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(nvmem_cell_put);
|
||||
|
||||
static inline void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell,
|
||||
void *buf)
|
||||
static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf)
|
||||
{
|
||||
u8 *p, *b;
|
||||
int i, bit_offset = cell->bit_offset;
|
||||
|
|
@ -1028,8 +1029,8 @@ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(nvmem_cell_read);
|
||||
|
||||
static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
|
||||
u8 *_buf, int len)
|
||||
static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
|
||||
u8 *_buf, int len)
|
||||
{
|
||||
struct nvmem_device *nvmem = cell->nvmem;
|
||||
int i, rc, nbits, bit_offset = cell->bit_offset;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ struct imx_iim_drvdata {
|
|||
struct iim_priv {
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
struct nvmem_config nvmem;
|
||||
};
|
||||
|
||||
static int imx_iim_read(void *context, unsigned int offset,
|
||||
|
|
@ -108,7 +107,7 @@ static int imx_iim_probe(struct platform_device *pdev)
|
|||
struct resource *res;
|
||||
struct iim_priv *iim;
|
||||
struct nvmem_device *nvmem;
|
||||
struct nvmem_config *cfg;
|
||||
struct nvmem_config cfg = {};
|
||||
const struct imx_iim_drvdata *drvdata = NULL;
|
||||
|
||||
iim = devm_kzalloc(dev, sizeof(*iim), GFP_KERNEL);
|
||||
|
|
@ -130,19 +129,16 @@ static int imx_iim_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(iim->clk))
|
||||
return PTR_ERR(iim->clk);
|
||||
|
||||
cfg = &iim->nvmem;
|
||||
cfg.name = "imx-iim",
|
||||
cfg.read_only = true,
|
||||
cfg.word_size = 1,
|
||||
cfg.stride = 1,
|
||||
cfg.reg_read = imx_iim_read,
|
||||
cfg.dev = dev;
|
||||
cfg.size = drvdata->nregs;
|
||||
cfg.priv = iim;
|
||||
|
||||
cfg->name = "imx-iim",
|
||||
cfg->read_only = true,
|
||||
cfg->word_size = 1,
|
||||
cfg->stride = 1,
|
||||
cfg->owner = THIS_MODULE,
|
||||
cfg->reg_read = imx_iim_read,
|
||||
cfg->dev = dev;
|
||||
cfg->size = drvdata->nregs;
|
||||
cfg->priv = iim;
|
||||
|
||||
nvmem = nvmem_register(cfg);
|
||||
nvmem = nvmem_register(&cfg);
|
||||
if (IS_ERR(nvmem))
|
||||
return PTR_ERR(nvmem);
|
||||
|
||||
|
|
|
|||
|
|
@ -40,14 +40,19 @@
|
|||
#define IMX_OCOTP_ADDR_CTRL_SET 0x0004
|
||||
#define IMX_OCOTP_ADDR_CTRL_CLR 0x0008
|
||||
#define IMX_OCOTP_ADDR_TIMING 0x0010
|
||||
#define IMX_OCOTP_ADDR_DATA 0x0020
|
||||
#define IMX_OCOTP_ADDR_DATA0 0x0020
|
||||
#define IMX_OCOTP_ADDR_DATA1 0x0030
|
||||
#define IMX_OCOTP_ADDR_DATA2 0x0040
|
||||
#define IMX_OCOTP_ADDR_DATA3 0x0050
|
||||
|
||||
#define IMX_OCOTP_BM_CTRL_ADDR 0x0000007F
|
||||
#define IMX_OCOTP_BM_CTRL_BUSY 0x00000100
|
||||
#define IMX_OCOTP_BM_CTRL_ERROR 0x00000200
|
||||
#define IMX_OCOTP_BM_CTRL_REL_SHADOWS 0x00000400
|
||||
|
||||
#define DEF_RELAX 20 /* > 16.5ns */
|
||||
#define DEF_RELAX 20 /* > 16.5ns */
|
||||
#define DEF_FSOURCE 1001 /* > 1000 ns */
|
||||
#define DEF_STROBE_PROG 10000 /* IPG clocks */
|
||||
#define IMX_OCOTP_WR_UNLOCK 0x3E770000
|
||||
#define IMX_OCOTP_READ_LOCKED_VAL 0xBADABADA
|
||||
|
||||
|
|
@ -57,10 +62,16 @@ struct ocotp_priv {
|
|||
struct device *dev;
|
||||
struct clk *clk;
|
||||
void __iomem *base;
|
||||
unsigned int nregs;
|
||||
const struct ocotp_params *params;
|
||||
struct nvmem_config *config;
|
||||
};
|
||||
|
||||
struct ocotp_params {
|
||||
unsigned int nregs;
|
||||
unsigned int bank_address_words;
|
||||
void (*set_timing)(struct ocotp_priv *priv);
|
||||
};
|
||||
|
||||
static int imx_ocotp_wait_for_busy(void __iomem *base, u32 flags)
|
||||
{
|
||||
int count;
|
||||
|
|
@ -121,8 +132,8 @@ static int imx_ocotp_read(void *context, unsigned int offset,
|
|||
index = offset >> 2;
|
||||
count = bytes >> 2;
|
||||
|
||||
if (count > (priv->nregs - index))
|
||||
count = priv->nregs - index;
|
||||
if (count > (priv->params->nregs - index))
|
||||
count = priv->params->nregs - index;
|
||||
|
||||
mutex_lock(&ocotp_mutex);
|
||||
|
||||
|
|
@ -160,32 +171,11 @@ read_end:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int imx_ocotp_write(void *context, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
static void imx_ocotp_set_imx6_timing(struct ocotp_priv *priv)
|
||||
{
|
||||
struct ocotp_priv *priv = context;
|
||||
u32 *buf = val;
|
||||
int ret;
|
||||
|
||||
unsigned long clk_rate = 0;
|
||||
unsigned long strobe_read, relax, strobe_prog;
|
||||
u32 timing = 0;
|
||||
u32 ctrl;
|
||||
u8 waddr;
|
||||
|
||||
/* allow only writing one complete OTP word at a time */
|
||||
if ((bytes != priv->config->word_size) ||
|
||||
(offset % priv->config->word_size))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&ocotp_mutex);
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&ocotp_mutex);
|
||||
dev_err(priv->dev, "failed to prepare/enable ocotp clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 47.3.1.3.1
|
||||
* Program HW_OCOTP_TIMING[STROBE_PROG] and HW_OCOTP_TIMING[RELAX]
|
||||
|
|
@ -204,6 +194,56 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val,
|
|||
timing |= (strobe_read << 16) & 0x003F0000;
|
||||
|
||||
writel(timing, priv->base + IMX_OCOTP_ADDR_TIMING);
|
||||
}
|
||||
|
||||
static void imx_ocotp_set_imx7_timing(struct ocotp_priv *priv)
|
||||
{
|
||||
unsigned long clk_rate = 0;
|
||||
u64 fsource, strobe_prog;
|
||||
u32 timing = 0;
|
||||
|
||||
/* i.MX 7Solo Applications Processor Reference Manual, Rev. 0.1
|
||||
* 6.4.3.3
|
||||
*/
|
||||
clk_rate = clk_get_rate(priv->clk);
|
||||
fsource = DIV_ROUND_UP_ULL((u64)clk_rate * DEF_FSOURCE,
|
||||
NSEC_PER_SEC) + 1;
|
||||
strobe_prog = DIV_ROUND_CLOSEST_ULL((u64)clk_rate * DEF_STROBE_PROG,
|
||||
NSEC_PER_SEC) + 1;
|
||||
|
||||
timing = strobe_prog & 0x00000FFF;
|
||||
timing |= (fsource << 12) & 0x000FF000;
|
||||
|
||||
writel(timing, priv->base + IMX_OCOTP_ADDR_TIMING);
|
||||
}
|
||||
|
||||
static int imx_ocotp_write(void *context, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
struct ocotp_priv *priv = context;
|
||||
u32 *buf = val;
|
||||
int ret;
|
||||
|
||||
u32 ctrl;
|
||||
u8 waddr;
|
||||
u8 word = 0;
|
||||
|
||||
/* allow only writing one complete OTP word at a time */
|
||||
if ((bytes != priv->config->word_size) ||
|
||||
(offset % priv->config->word_size))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&ocotp_mutex);
|
||||
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&ocotp_mutex);
|
||||
dev_err(priv->dev, "failed to prepare/enable ocotp clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Setup the write timing values */
|
||||
priv->params->set_timing(priv);
|
||||
|
||||
/* 47.3.1.3.2
|
||||
* Check that HW_OCOTP_CTRL[BUSY] and HW_OCOTP_CTRL[ERROR] are clear.
|
||||
|
|
@ -224,8 +264,23 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val,
|
|||
* description. Both the unlock code and address can be written in the
|
||||
* same operation.
|
||||
*/
|
||||
/* OTP write/read address specifies one of 128 word address locations */
|
||||
waddr = offset / 4;
|
||||
if (priv->params->bank_address_words != 0) {
|
||||
/*
|
||||
* In banked/i.MX7 mode the OTP register bank goes into waddr
|
||||
* see i.MX 7Solo Applications Processor Reference Manual, Rev.
|
||||
* 0.1 section 6.4.3.1
|
||||
*/
|
||||
offset = offset / priv->config->word_size;
|
||||
waddr = offset / priv->params->bank_address_words;
|
||||
word = offset & (priv->params->bank_address_words - 1);
|
||||
} else {
|
||||
/*
|
||||
* Non-banked i.MX6 mode.
|
||||
* OTP write/read address specifies one of 128 word address
|
||||
* locations
|
||||
*/
|
||||
waddr = offset / 4;
|
||||
}
|
||||
|
||||
ctrl = readl(priv->base + IMX_OCOTP_ADDR_CTRL);
|
||||
ctrl &= ~IMX_OCOTP_BM_CTRL_ADDR;
|
||||
|
|
@ -251,8 +306,43 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val,
|
|||
* shift right (with zero fill). This shifting is required to program
|
||||
* the OTP serially. During the write operation, HW_OCOTP_DATA cannot be
|
||||
* modified.
|
||||
* Note: on i.MX7 there are four data fields to write for banked write
|
||||
* with the fuse blowing operation only taking place after data0
|
||||
* has been written. This is why data0 must always be the last
|
||||
* register written.
|
||||
*/
|
||||
writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA);
|
||||
if (priv->params->bank_address_words != 0) {
|
||||
/* Banked/i.MX7 mode */
|
||||
switch (word) {
|
||||
case 0:
|
||||
writel(0, priv->base + IMX_OCOTP_ADDR_DATA1);
|
||||
writel(0, priv->base + IMX_OCOTP_ADDR_DATA2);
|
||||
writel(0, priv->base + IMX_OCOTP_ADDR_DATA3);
|
||||
writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA0);
|
||||
break;
|
||||
case 1:
|
||||
writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA1);
|
||||
writel(0, priv->base + IMX_OCOTP_ADDR_DATA2);
|
||||
writel(0, priv->base + IMX_OCOTP_ADDR_DATA3);
|
||||
writel(0, priv->base + IMX_OCOTP_ADDR_DATA0);
|
||||
break;
|
||||
case 2:
|
||||
writel(0, priv->base + IMX_OCOTP_ADDR_DATA1);
|
||||
writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA2);
|
||||
writel(0, priv->base + IMX_OCOTP_ADDR_DATA3);
|
||||
writel(0, priv->base + IMX_OCOTP_ADDR_DATA0);
|
||||
break;
|
||||
case 3:
|
||||
writel(0, priv->base + IMX_OCOTP_ADDR_DATA1);
|
||||
writel(0, priv->base + IMX_OCOTP_ADDR_DATA2);
|
||||
writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA3);
|
||||
writel(0, priv->base + IMX_OCOTP_ADDR_DATA0);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Non-banked i.MX6 mode */
|
||||
writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA0);
|
||||
}
|
||||
|
||||
/* 47.4.1.4.5
|
||||
* Once complete, the controller will clear BUSY. A write request to a
|
||||
|
|
@ -303,17 +393,46 @@ static struct nvmem_config imx_ocotp_nvmem_config = {
|
|||
.read_only = false,
|
||||
.word_size = 4,
|
||||
.stride = 4,
|
||||
.owner = THIS_MODULE,
|
||||
.reg_read = imx_ocotp_read,
|
||||
.reg_write = imx_ocotp_write,
|
||||
};
|
||||
|
||||
static const struct ocotp_params imx6q_params = {
|
||||
.nregs = 128,
|
||||
.bank_address_words = 0,
|
||||
.set_timing = imx_ocotp_set_imx6_timing,
|
||||
};
|
||||
|
||||
static const struct ocotp_params imx6sl_params = {
|
||||
.nregs = 64,
|
||||
.bank_address_words = 0,
|
||||
.set_timing = imx_ocotp_set_imx6_timing,
|
||||
};
|
||||
|
||||
static const struct ocotp_params imx6sx_params = {
|
||||
.nregs = 128,
|
||||
.bank_address_words = 0,
|
||||
.set_timing = imx_ocotp_set_imx6_timing,
|
||||
};
|
||||
|
||||
static const struct ocotp_params imx6ul_params = {
|
||||
.nregs = 128,
|
||||
.bank_address_words = 0,
|
||||
.set_timing = imx_ocotp_set_imx6_timing,
|
||||
};
|
||||
|
||||
static const struct ocotp_params imx7d_params = {
|
||||
.nregs = 64,
|
||||
.bank_address_words = 4,
|
||||
.set_timing = imx_ocotp_set_imx7_timing,
|
||||
};
|
||||
|
||||
static const struct of_device_id imx_ocotp_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx6q-ocotp", (void *)128 },
|
||||
{ .compatible = "fsl,imx6sl-ocotp", (void *)64 },
|
||||
{ .compatible = "fsl,imx6sx-ocotp", (void *)128 },
|
||||
{ .compatible = "fsl,imx6ul-ocotp", (void *)128 },
|
||||
{ .compatible = "fsl,imx7d-ocotp", (void *)64 },
|
||||
{ .compatible = "fsl,imx6q-ocotp", .data = &imx6q_params },
|
||||
{ .compatible = "fsl,imx6sl-ocotp", .data = &imx6sl_params },
|
||||
{ .compatible = "fsl,imx6sx-ocotp", .data = &imx6sx_params },
|
||||
{ .compatible = "fsl,imx6ul-ocotp", .data = &imx6ul_params },
|
||||
{ .compatible = "fsl,imx7d-ocotp", .data = &imx7d_params },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);
|
||||
|
|
@ -342,8 +461,8 @@ static int imx_ocotp_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(priv->clk);
|
||||
|
||||
of_id = of_match_device(imx_ocotp_dt_ids, dev);
|
||||
priv->nregs = (unsigned long)of_id->data;
|
||||
imx_ocotp_nvmem_config.size = 4 * priv->nregs;
|
||||
priv->params = of_device_get_match_data(&pdev->dev);
|
||||
imx_ocotp_nvmem_config.size = 4 * priv->params->nregs;
|
||||
imx_ocotp_nvmem_config.dev = dev;
|
||||
imx_ocotp_nvmem_config.priv = priv;
|
||||
priv->config = &imx_ocotp_nvmem_config;
|
||||
|
|
@ -375,5 +494,5 @@ static struct platform_driver imx_ocotp_driver = {
|
|||
module_platform_driver(imx_ocotp_driver);
|
||||
|
||||
MODULE_AUTHOR("Philipp Zabel <[email protected]>");
|
||||
MODULE_DESCRIPTION("i.MX6 OCOTP fuse box driver");
|
||||
MODULE_DESCRIPTION("i.MX6/i.MX7 OCOTP fuse box driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
|||
|
|
@ -159,7 +159,6 @@ static struct nvmem_config lpc18xx_nvmem_config = {
|
|||
.word_size = 4,
|
||||
.reg_read = lpc18xx_eeprom_read,
|
||||
.reg_write = lpc18xx_eeprom_gather_write,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int lpc18xx_eeprom_probe(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ static struct nvmem_config lpc18xx_otp_nvmem_config = {
|
|||
.read_only = true,
|
||||
.word_size = LPC18XX_OTP_WORD_SIZE,
|
||||
.stride = LPC18XX_OTP_WORD_SIZE,
|
||||
.owner = THIS_MODULE,
|
||||
.reg_read = lpc18xx_otp_read,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Amlogic eFuse Driver
|
||||
* Amlogic Meson GX eFuse Driver
|
||||
*
|
||||
* Copyright (c) 2016 Endless Computers, Inc.
|
||||
* Author: Carlo Caione <carlo@endlessm.com>
|
||||
|
|
@ -37,7 +37,6 @@ static int meson_efuse_read(void *context, unsigned int offset,
|
|||
|
||||
static struct nvmem_config econfig = {
|
||||
.name = "meson-efuse",
|
||||
.owner = THIS_MODULE,
|
||||
.stride = 1,
|
||||
.word_size = 1,
|
||||
.read_only = true,
|
||||
|
|
@ -89,5 +88,5 @@ static struct platform_driver meson_efuse_driver = {
|
|||
module_platform_driver(meson_efuse_driver);
|
||||
|
||||
MODULE_AUTHOR("Carlo Caione <[email protected]>");
|
||||
MODULE_DESCRIPTION("Amlogic Meson NVMEM driver");
|
||||
MODULE_DESCRIPTION("Amlogic Meson GX NVMEM driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
|||
265
drivers/nvmem/meson-mx-efuse.c
Normal file
265
drivers/nvmem/meson-mx-efuse.c
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* Amlogic Meson6, Meson8 and Meson8b eFuse Driver
|
||||
*
|
||||
* Copyright (c) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-provider.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define MESON_MX_EFUSE_CNTL1 0x04
|
||||
#define MESON_MX_EFUSE_CNTL1_PD_ENABLE BIT(27)
|
||||
#define MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY BIT(26)
|
||||
#define MESON_MX_EFUSE_CNTL1_AUTO_RD_START BIT(25)
|
||||
#define MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE BIT(24)
|
||||
#define MESON_MX_EFUSE_CNTL1_BYTE_WR_DATA GENMASK(23, 16)
|
||||
#define MESON_MX_EFUSE_CNTL1_AUTO_WR_BUSY BIT(14)
|
||||
#define MESON_MX_EFUSE_CNTL1_AUTO_WR_START BIT(13)
|
||||
#define MESON_MX_EFUSE_CNTL1_AUTO_WR_ENABLE BIT(12)
|
||||
#define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET BIT(11)
|
||||
#define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK GENMASK(10, 0)
|
||||
|
||||
#define MESON_MX_EFUSE_CNTL2 0x08
|
||||
|
||||
#define MESON_MX_EFUSE_CNTL4 0x10
|
||||
#define MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE BIT(10)
|
||||
|
||||
struct meson_mx_efuse_platform_data {
|
||||
const char *name;
|
||||
unsigned int word_size;
|
||||
};
|
||||
|
||||
struct meson_mx_efuse {
|
||||
void __iomem *base;
|
||||
struct clk *core_clk;
|
||||
struct nvmem_device *nvmem;
|
||||
struct nvmem_config config;
|
||||
};
|
||||
|
||||
static void meson_mx_efuse_mask_bits(struct meson_mx_efuse *efuse, u32 reg,
|
||||
u32 mask, u32 set)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
data = readl(efuse->base + reg);
|
||||
data &= ~mask;
|
||||
data |= (set & mask);
|
||||
|
||||
writel(data, efuse->base + reg);
|
||||
}
|
||||
|
||||
static int meson_mx_efuse_hw_enable(struct meson_mx_efuse *efuse)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(efuse->core_clk);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* power up the efuse */
|
||||
meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
|
||||
MESON_MX_EFUSE_CNTL1_PD_ENABLE, 0);
|
||||
|
||||
meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL4,
|
||||
MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_mx_efuse_hw_disable(struct meson_mx_efuse *efuse)
|
||||
{
|
||||
meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
|
||||
MESON_MX_EFUSE_CNTL1_PD_ENABLE,
|
||||
MESON_MX_EFUSE_CNTL1_PD_ENABLE);
|
||||
|
||||
clk_disable_unprepare(efuse->core_clk);
|
||||
}
|
||||
|
||||
static int meson_mx_efuse_read_addr(struct meson_mx_efuse *efuse,
|
||||
unsigned int addr, u32 *value)
|
||||
{
|
||||
int err;
|
||||
u32 regval;
|
||||
|
||||
/* write the address to read */
|
||||
regval = FIELD_PREP(MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, addr);
|
||||
meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
|
||||
MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, regval);
|
||||
|
||||
/* inform the hardware that we changed the address */
|
||||
meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
|
||||
MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET,
|
||||
MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET);
|
||||
meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
|
||||
MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET, 0);
|
||||
|
||||
/* start the read process */
|
||||
meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
|
||||
MESON_MX_EFUSE_CNTL1_AUTO_RD_START,
|
||||
MESON_MX_EFUSE_CNTL1_AUTO_RD_START);
|
||||
meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
|
||||
MESON_MX_EFUSE_CNTL1_AUTO_RD_START, 0);
|
||||
|
||||
/*
|
||||
* perform a dummy read to ensure that the HW has the RD_BUSY bit set
|
||||
* when polling for the status below.
|
||||
*/
|
||||
readl(efuse->base + MESON_MX_EFUSE_CNTL1);
|
||||
|
||||
err = readl_poll_timeout_atomic(efuse->base + MESON_MX_EFUSE_CNTL1,
|
||||
regval,
|
||||
(!(regval & MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY)),
|
||||
1, 1000);
|
||||
if (err) {
|
||||
dev_err(efuse->config.dev,
|
||||
"Timeout while reading efuse address %u\n", addr);
|
||||
return err;
|
||||
}
|
||||
|
||||
*value = readl(efuse->base + MESON_MX_EFUSE_CNTL2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_mx_efuse_read(void *context, unsigned int offset,
|
||||
void *buf, size_t bytes)
|
||||
{
|
||||
struct meson_mx_efuse *efuse = context;
|
||||
u32 tmp;
|
||||
int err, i, addr;
|
||||
|
||||
err = meson_mx_efuse_hw_enable(efuse);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
|
||||
MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE,
|
||||
MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE);
|
||||
|
||||
for (i = offset; i < offset + bytes; i += efuse->config.word_size) {
|
||||
addr = i / efuse->config.word_size;
|
||||
|
||||
err = meson_mx_efuse_read_addr(efuse, addr, &tmp);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
memcpy(buf + i, &tmp, efuse->config.word_size);
|
||||
}
|
||||
|
||||
meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,
|
||||
MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE, 0);
|
||||
|
||||
meson_mx_efuse_hw_disable(efuse);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct meson_mx_efuse_platform_data meson6_efuse_data = {
|
||||
.name = "meson6-efuse",
|
||||
.word_size = 1,
|
||||
};
|
||||
|
||||
static const struct meson_mx_efuse_platform_data meson8_efuse_data = {
|
||||
.name = "meson8-efuse",
|
||||
.word_size = 4,
|
||||
};
|
||||
|
||||
static const struct meson_mx_efuse_platform_data meson8b_efuse_data = {
|
||||
.name = "meson8b-efuse",
|
||||
.word_size = 4,
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_mx_efuse_match[] = {
|
||||
{ .compatible = "amlogic,meson6-efuse", .data = &meson6_efuse_data },
|
||||
{ .compatible = "amlogic,meson8-efuse", .data = &meson8_efuse_data },
|
||||
{ .compatible = "amlogic,meson8b-efuse", .data = &meson8b_efuse_data },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, meson_mx_efuse_match);
|
||||
|
||||
static int meson_mx_efuse_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct meson_mx_efuse_platform_data *drvdata;
|
||||
struct meson_mx_efuse *efuse;
|
||||
struct resource *res;
|
||||
|
||||
drvdata = of_device_get_match_data(&pdev->dev);
|
||||
if (!drvdata)
|
||||
return -EINVAL;
|
||||
|
||||
efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL);
|
||||
if (!efuse)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
efuse->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(efuse->base))
|
||||
return PTR_ERR(efuse->base);
|
||||
|
||||
efuse->config.name = devm_kstrdup(&pdev->dev, drvdata->name,
|
||||
GFP_KERNEL);
|
||||
efuse->config.owner = THIS_MODULE;
|
||||
efuse->config.dev = &pdev->dev;
|
||||
efuse->config.priv = efuse;
|
||||
efuse->config.stride = drvdata->word_size;
|
||||
efuse->config.word_size = drvdata->word_size;
|
||||
efuse->config.size = SZ_512;
|
||||
efuse->config.read_only = true;
|
||||
efuse->config.reg_read = meson_mx_efuse_read;
|
||||
|
||||
efuse->core_clk = devm_clk_get(&pdev->dev, "core");
|
||||
if (IS_ERR(efuse->core_clk)) {
|
||||
dev_err(&pdev->dev, "Failed to get core clock\n");
|
||||
return PTR_ERR(efuse->core_clk);
|
||||
}
|
||||
|
||||
efuse->nvmem = nvmem_register(&efuse->config);
|
||||
if (IS_ERR(efuse->nvmem))
|
||||
return PTR_ERR(efuse->nvmem);
|
||||
|
||||
platform_set_drvdata(pdev, efuse);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_mx_efuse_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct meson_mx_efuse *efuse = platform_get_drvdata(pdev);
|
||||
|
||||
return nvmem_unregister(efuse->nvmem);
|
||||
}
|
||||
|
||||
static struct platform_driver meson_mx_efuse_driver = {
|
||||
.probe = meson_mx_efuse_probe,
|
||||
.remove = meson_mx_efuse_remove,
|
||||
.driver = {
|
||||
.name = "meson-mx-efuse",
|
||||
.of_match_table = meson_mx_efuse_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(meson_mx_efuse_driver);
|
||||
|
||||
MODULE_AUTHOR("Martin Blumenstingl <[email protected]>");
|
||||
MODULE_DESCRIPTION("Amlogic Meson MX eFuse NVMEM driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -18,15 +18,19 @@
|
|||
#include <linux/nvmem-provider.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct mtk_efuse_priv {
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
static int mtk_reg_read(void *context,
|
||||
unsigned int reg, void *_val, size_t bytes)
|
||||
{
|
||||
void __iomem *base = context;
|
||||
struct mtk_efuse_priv *priv = context;
|
||||
u32 *val = _val;
|
||||
int i = 0, words = bytes / 4;
|
||||
|
||||
while (words--)
|
||||
*val++ = readl(base + reg + (i++ * 4));
|
||||
*val++ = readl(priv->base + reg + (i++ * 4));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -34,12 +38,12 @@ static int mtk_reg_read(void *context,
|
|||
static int mtk_reg_write(void *context,
|
||||
unsigned int reg, void *_val, size_t bytes)
|
||||
{
|
||||
void __iomem *base = context;
|
||||
struct mtk_efuse_priv *priv = context;
|
||||
u32 *val = _val;
|
||||
int i = 0, words = bytes / 4;
|
||||
|
||||
while (words--)
|
||||
writel(*val++, base + reg + (i++ * 4));
|
||||
writel(*val++, priv->base + reg + (i++ * 4));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -49,27 +53,26 @@ static int mtk_efuse_probe(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct nvmem_device *nvmem;
|
||||
struct nvmem_config *econfig;
|
||||
void __iomem *base;
|
||||
struct nvmem_config econfig = {};
|
||||
struct mtk_efuse_priv *priv;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
econfig = devm_kzalloc(dev, sizeof(*econfig), GFP_KERNEL);
|
||||
if (!econfig)
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
econfig->stride = 4;
|
||||
econfig->word_size = 4;
|
||||
econfig->reg_read = mtk_reg_read;
|
||||
econfig->reg_write = mtk_reg_write;
|
||||
econfig->size = resource_size(res);
|
||||
econfig->priv = base;
|
||||
econfig->dev = dev;
|
||||
econfig->owner = THIS_MODULE;
|
||||
nvmem = nvmem_register(econfig);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
econfig.stride = 4;
|
||||
econfig.word_size = 4;
|
||||
econfig.reg_read = mtk_reg_read;
|
||||
econfig.reg_write = mtk_reg_write;
|
||||
econfig.size = resource_size(res);
|
||||
econfig.priv = priv;
|
||||
econfig.dev = dev;
|
||||
nvmem = nvmem_register(&econfig);
|
||||
if (IS_ERR(nvmem))
|
||||
return PTR_ERR(nvmem);
|
||||
|
||||
|
|
|
|||
|
|
@ -118,7 +118,6 @@ static struct nvmem_config ocotp_config = {
|
|||
.name = "mxs-ocotp",
|
||||
.stride = 16,
|
||||
.word_size = 4,
|
||||
.owner = THIS_MODULE,
|
||||
.reg_read = mxs_ocotp_read,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -17,15 +17,19 @@
|
|||
#include <linux/nvmem-provider.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct qfprom_priv {
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
static int qfprom_reg_read(void *context,
|
||||
unsigned int reg, void *_val, size_t bytes)
|
||||
{
|
||||
void __iomem *base = context;
|
||||
struct qfprom_priv *priv = context;
|
||||
u8 *val = _val;
|
||||
int i = 0, words = bytes;
|
||||
|
||||
while (words--)
|
||||
*val++ = readb(base + reg + i++);
|
||||
*val++ = readb(priv->base + reg + i++);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -33,12 +37,12 @@ static int qfprom_reg_read(void *context,
|
|||
static int qfprom_reg_write(void *context,
|
||||
unsigned int reg, void *_val, size_t bytes)
|
||||
{
|
||||
void __iomem *base = context;
|
||||
struct qfprom_priv *priv = context;
|
||||
u8 *val = _val;
|
||||
int i = 0, words = bytes;
|
||||
|
||||
while (words--)
|
||||
writeb(*val++, base + reg + i++);
|
||||
writeb(*val++, priv->base + reg + i++);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -52,7 +56,6 @@ static int qfprom_remove(struct platform_device *pdev)
|
|||
|
||||
static struct nvmem_config econfig = {
|
||||
.name = "qfprom",
|
||||
.owner = THIS_MODULE,
|
||||
.stride = 1,
|
||||
.word_size = 1,
|
||||
.reg_read = qfprom_reg_read,
|
||||
|
|
@ -64,16 +67,20 @@ static int qfprom_probe(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct nvmem_device *nvmem;
|
||||
void __iomem *base;
|
||||
struct qfprom_priv *priv;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
priv->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
econfig.size = resource_size(res);
|
||||
econfig.dev = dev;
|
||||
econfig.priv = base;
|
||||
econfig.priv = priv;
|
||||
|
||||
nvmem = nvmem_register(&econfig);
|
||||
if (IS_ERR(nvmem))
|
||||
|
|
|
|||
|
|
@ -149,7 +149,6 @@ static int rockchip_rk3399_efuse_read(void *context, unsigned int offset,
|
|||
|
||||
static struct nvmem_config econfig = {
|
||||
.name = "rockchip-efuse",
|
||||
.owner = THIS_MODULE,
|
||||
.stride = 1,
|
||||
.word_size = 1,
|
||||
.read_only = true,
|
||||
|
|
@ -177,6 +176,10 @@ static const struct of_device_id rockchip_efuse_match[] = {
|
|||
.compatible = "rockchip,rk3288-efuse",
|
||||
.data = (void *)&rockchip_rk3288_efuse_read,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3368-efuse",
|
||||
.data = (void *)&rockchip_rk3288_efuse_read,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3399-efuse",
|
||||
.data = (void *)&rockchip_rk3399_efuse_read,
|
||||
|
|
|
|||
156
drivers/nvmem/snvs_lpgpr.c
Normal file
156
drivers/nvmem/snvs_lpgpr.c
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
|
||||
* Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
|
||||
*
|
||||
* 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 published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-provider.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define IMX6Q_SNVS_HPLR 0x00
|
||||
#define IMX6Q_GPR_SL BIT(5)
|
||||
#define IMX6Q_SNVS_LPLR 0x34
|
||||
#define IMX6Q_GPR_HL BIT(5)
|
||||
#define IMX6Q_SNVS_LPGPR 0x68
|
||||
|
||||
struct snvs_lpgpr_cfg {
|
||||
int offset;
|
||||
int offset_hplr;
|
||||
int offset_lplr;
|
||||
};
|
||||
|
||||
struct snvs_lpgpr_priv {
|
||||
struct device_d *dev;
|
||||
struct regmap *regmap;
|
||||
struct nvmem_config cfg;
|
||||
const struct snvs_lpgpr_cfg *dcfg;
|
||||
};
|
||||
|
||||
static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q = {
|
||||
.offset = IMX6Q_SNVS_LPGPR,
|
||||
.offset_hplr = IMX6Q_SNVS_HPLR,
|
||||
.offset_lplr = IMX6Q_SNVS_LPLR,
|
||||
};
|
||||
|
||||
static int snvs_lpgpr_write(void *context, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
struct snvs_lpgpr_priv *priv = context;
|
||||
const struct snvs_lpgpr_cfg *dcfg = priv->dcfg;
|
||||
unsigned int lock_reg;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(priv->regmap, dcfg->offset_hplr, &lock_reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (lock_reg & IMX6Q_GPR_SL)
|
||||
return -EPERM;
|
||||
|
||||
ret = regmap_read(priv->regmap, dcfg->offset_lplr, &lock_reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (lock_reg & IMX6Q_GPR_HL)
|
||||
return -EPERM;
|
||||
|
||||
return regmap_bulk_write(priv->regmap, dcfg->offset + offset, val,
|
||||
bytes / 4);
|
||||
}
|
||||
|
||||
static int snvs_lpgpr_read(void *context, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
struct snvs_lpgpr_priv *priv = context;
|
||||
const struct snvs_lpgpr_cfg *dcfg = priv->dcfg;
|
||||
|
||||
return regmap_bulk_read(priv->regmap, dcfg->offset + offset,
|
||||
val, bytes / 4);
|
||||
}
|
||||
|
||||
static int snvs_lpgpr_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct device_node *syscon_node;
|
||||
struct snvs_lpgpr_priv *priv;
|
||||
struct nvmem_config *cfg;
|
||||
struct nvmem_device *nvmem;
|
||||
const struct snvs_lpgpr_cfg *dcfg;
|
||||
|
||||
if (!node)
|
||||
return -ENOENT;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
dcfg = of_device_get_match_data(dev);
|
||||
if (!dcfg)
|
||||
return -EINVAL;
|
||||
|
||||
syscon_node = of_get_parent(node);
|
||||
if (!syscon_node)
|
||||
return -ENODEV;
|
||||
|
||||
priv->regmap = syscon_node_to_regmap(syscon_node);
|
||||
of_node_put(syscon_node);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
priv->dcfg = dcfg;
|
||||
|
||||
cfg = &priv->cfg;
|
||||
cfg->priv = priv;
|
||||
cfg->name = dev_name(dev);
|
||||
cfg->dev = dev;
|
||||
cfg->stride = 4,
|
||||
cfg->word_size = 4,
|
||||
cfg->size = 4,
|
||||
cfg->owner = THIS_MODULE,
|
||||
cfg->reg_read = snvs_lpgpr_read,
|
||||
cfg->reg_write = snvs_lpgpr_write,
|
||||
|
||||
nvmem = nvmem_register(cfg);
|
||||
if (IS_ERR(nvmem))
|
||||
return PTR_ERR(nvmem);
|
||||
|
||||
platform_set_drvdata(pdev, nvmem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snvs_lpgpr_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct nvmem_device *nvmem = platform_get_drvdata(pdev);
|
||||
|
||||
return nvmem_unregister(nvmem);
|
||||
}
|
||||
|
||||
static const struct of_device_id snvs_lpgpr_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx6q-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx6q },
|
||||
{ .compatible = "fsl,imx6ul-snvs-lpgpr",
|
||||
.data = &snvs_lpgpr_cfg_imx6q },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, snvs_lpgpr_dt_ids);
|
||||
|
||||
static struct platform_driver snvs_lpgpr_driver = {
|
||||
.probe = snvs_lpgpr_probe,
|
||||
.remove = snvs_lpgpr_remove,
|
||||
.driver = {
|
||||
.name = "snvs_lpgpr",
|
||||
.of_match_table = snvs_lpgpr_dt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(snvs_lpgpr_driver);
|
||||
|
||||
MODULE_AUTHOR("Oleksij Rempel <[email protected]>");
|
||||
MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 Secure Non-Volatile Storage");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -40,7 +40,6 @@ static struct nvmem_config econfig = {
|
|||
.read_only = true,
|
||||
.stride = 4,
|
||||
.word_size = 1,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
struct sunxi_sid_cfg {
|
||||
|
|
@ -199,10 +198,16 @@ static const struct sunxi_sid_cfg sun8i_h3_cfg = {
|
|||
.need_register_readout = true,
|
||||
};
|
||||
|
||||
static const struct sunxi_sid_cfg sun50i_a64_cfg = {
|
||||
.value_offset = 0x200,
|
||||
.size = 0x100,
|
||||
};
|
||||
|
||||
static const struct of_device_id sunxi_sid_of_match[] = {
|
||||
{ .compatible = "allwinner,sun4i-a10-sid", .data = &sun4i_a10_cfg },
|
||||
{ .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg },
|
||||
{ .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg },
|
||||
{ .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg },
|
||||
{/* sentinel */},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sunxi_sid_of_match);
|
||||
|
|
|
|||
97
drivers/nvmem/uniphier-efuse.c
Normal file
97
drivers/nvmem/uniphier-efuse.c
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* UniPhier eFuse driver
|
||||
*
|
||||
* Copyright (C) 2017 Socionext Inc.
|
||||
*
|
||||
* 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
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-provider.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct uniphier_efuse_priv {
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
static int uniphier_reg_read(void *context,
|
||||
unsigned int reg, void *_val, size_t bytes)
|
||||
{
|
||||
struct uniphier_efuse_priv *priv = context;
|
||||
u32 *val = _val;
|
||||
int offs;
|
||||
|
||||
for (offs = 0; offs < bytes; offs += sizeof(u32))
|
||||
*val++ = readl(priv->base + reg + offs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_efuse_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
struct nvmem_device *nvmem;
|
||||
struct nvmem_config econfig = {};
|
||||
struct uniphier_efuse_priv *priv;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
econfig.stride = 4;
|
||||
econfig.word_size = 4;
|
||||
econfig.read_only = true;
|
||||
econfig.reg_read = uniphier_reg_read;
|
||||
econfig.size = resource_size(res);
|
||||
econfig.priv = priv;
|
||||
econfig.dev = dev;
|
||||
nvmem = nvmem_register(&econfig);
|
||||
if (IS_ERR(nvmem))
|
||||
return PTR_ERR(nvmem);
|
||||
|
||||
platform_set_drvdata(pdev, nvmem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uniphier_efuse_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct nvmem_device *nvmem = platform_get_drvdata(pdev);
|
||||
|
||||
return nvmem_unregister(nvmem);
|
||||
}
|
||||
|
||||
static const struct of_device_id uniphier_efuse_of_match[] = {
|
||||
{ .compatible = "socionext,uniphier-efuse",},
|
||||
{/* sentinel */},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, uniphier_efuse_of_match);
|
||||
|
||||
static struct platform_driver uniphier_efuse_driver = {
|
||||
.probe = uniphier_efuse_probe,
|
||||
.remove = uniphier_efuse_remove,
|
||||
.driver = {
|
||||
.name = "uniphier-efuse",
|
||||
.of_match_table = uniphier_efuse_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(uniphier_efuse_driver);
|
||||
|
||||
MODULE_AUTHOR("Keiji Hayashibara <[email protected]>");
|
||||
MODULE_DESCRIPTION("UniPhier eFuse driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -206,7 +206,6 @@ static int vf610_ocotp_read(void *context, unsigned int offset,
|
|||
|
||||
static struct nvmem_config ocotp_config = {
|
||||
.name = "ocotp",
|
||||
.owner = THIS_MODULE,
|
||||
.stride = 4,
|
||||
.word_size = 4,
|
||||
.reg_read = vf610_ocotp_read,
|
||||
|
|
|
|||
Loading…
Reference in a new issue