diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-16 12:10:59 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-16 12:10:59 -0500 |
| commit | 2bf16b7a73caf3435f782e4170cfe563675e10f9 (patch) | |
| tree | 7f4c5b28a02f08c4d6fd69dd43db5872b07c20c4 /drivers/nvmem | |
| parent | b9743042b3d31fed271ae19aee79dd86817904f0 (diff) | |
| parent | f13d1a8a801dae552ef495c84a223280586a9f67 (diff) | |
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
...
Diffstat (limited to 'drivers/nvmem')
| -rw-r--r-- | drivers/nvmem/Kconfig | 35 | ||||
| -rw-r--r-- | drivers/nvmem/Makefile | 6 | ||||
| -rw-r--r-- | drivers/nvmem/bcm-ocotp.c | 1 | ||||
| -rw-r--r-- | drivers/nvmem/core.c | 13 | ||||
| -rw-r--r-- | drivers/nvmem/imx-iim.c | 24 | ||||
| -rw-r--r-- | drivers/nvmem/imx-ocotp.c | 193 | ||||
| -rw-r--r-- | drivers/nvmem/lpc18xx_eeprom.c | 1 | ||||
| -rw-r--r-- | drivers/nvmem/lpc18xx_otp.c | 1 | ||||
| -rw-r--r-- | drivers/nvmem/meson-efuse.c | 5 | ||||
| -rw-r--r-- | drivers/nvmem/meson-mx-efuse.c | 265 | ||||
| -rw-r--r-- | drivers/nvmem/mtk-efuse.c | 47 | ||||
| -rw-r--r-- | drivers/nvmem/mxs-ocotp.c | 1 | ||||
| -rw-r--r-- | drivers/nvmem/qfprom.c | 27 | ||||
| -rw-r--r-- | drivers/nvmem/rockchip-efuse.c | 5 | ||||
| -rw-r--r-- | drivers/nvmem/snvs_lpgpr.c | 156 | ||||
| -rw-r--r-- | drivers/nvmem/sunxi_sid.c | 7 | ||||
| -rw-r--r-- | drivers/nvmem/uniphier-efuse.c | 97 | ||||
| -rw-r--r-- | drivers/nvmem/vf610-ocotp.c | 1 |
18 files changed, 784 insertions, 101 deletions
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 101ced4c84be..ff505af064ba 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig | |||
| @@ -123,6 +123,17 @@ config NVMEM_SUNXI_SID | |||
| 123 | This driver can also be built as a module. If so, the module | 123 | This driver can also be built as a module. If so, the module |
| 124 | will be called nvmem_sunxi_sid. | 124 | will be called nvmem_sunxi_sid. |
| 125 | 125 | ||
| 126 | config UNIPHIER_EFUSE | ||
| 127 | tristate "UniPhier SoCs eFuse support" | ||
| 128 | depends on ARCH_UNIPHIER || COMPILE_TEST | ||
| 129 | depends on HAS_IOMEM | ||
| 130 | help | ||
| 131 | This is a simple driver to dump specified values of UniPhier SoC | ||
| 132 | from eFuse. | ||
| 133 | |||
| 134 | This driver can also be built as a module. If so, the module | ||
| 135 | will be called nvmem-uniphier-efuse. | ||
| 136 | |||
| 126 | config NVMEM_VF610_OCOTP | 137 | config NVMEM_VF610_OCOTP |
| 127 | tristate "VF610 SoC OCOTP support" | 138 | tristate "VF610 SoC OCOTP support" |
| 128 | depends on SOC_VF610 || COMPILE_TEST | 139 | depends on SOC_VF610 || COMPILE_TEST |
| @@ -135,13 +146,33 @@ config NVMEM_VF610_OCOTP | |||
| 135 | be called nvmem-vf610-ocotp. | 146 | be called nvmem-vf610-ocotp. |
| 136 | 147 | ||
| 137 | config MESON_EFUSE | 148 | config MESON_EFUSE |
| 138 | tristate "Amlogic eFuse Support" | 149 | tristate "Amlogic Meson GX eFuse Support" |
| 139 | depends on (ARCH_MESON || COMPILE_TEST) && MESON_SM | 150 | depends on (ARCH_MESON || COMPILE_TEST) && MESON_SM |
| 140 | help | 151 | help |
| 141 | This is a driver to retrieve specific values from the eFuse found on | 152 | This is a driver to retrieve specific values from the eFuse found on |
| 142 | the Amlogic Meson SoCs. | 153 | the Amlogic Meson GX SoCs. |
| 143 | 154 | ||
| 144 | This driver can also be built as a module. If so, the module | 155 | This driver can also be built as a module. If so, the module |
| 145 | will be called nvmem_meson_efuse. | 156 | will be called nvmem_meson_efuse. |
| 146 | 157 | ||
| 158 | config MESON_MX_EFUSE | ||
| 159 | tristate "Amlogic Meson6/Meson8/Meson8b eFuse Support" | ||
| 160 | depends on ARCH_MESON || COMPILE_TEST | ||
| 161 | help | ||
| 162 | This is a driver to retrieve specific values from the eFuse found on | ||
| 163 | the Amlogic Meson6, Meson8 and Meson8b SoCs. | ||
| 164 | |||
| 165 | This driver can also be built as a module. If so, the module | ||
| 166 | will be called nvmem_meson_mx_efuse. | ||
| 167 | |||
| 168 | config NVMEM_SNVS_LPGPR | ||
| 169 | tristate "Support for Low Power General Purpose Register" | ||
| 170 | depends on SOC_IMX6 || COMPILE_TEST | ||
| 171 | help | ||
| 172 | This is a driver for Low Power General Purpose Register (LPGPR) available on | ||
| 173 | i.MX6 SoCs in Secure Non-Volatile Storage (SNVS) of this chip. | ||
| 174 | |||
| 175 | This driver can also be built as a module. If so, the module | ||
| 176 | will be called nvmem-snvs-lpgpr. | ||
| 177 | |||
| 147 | endif | 178 | endif |
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 6f7a77fb3ee7..e54dcfa6565a 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile | |||
| @@ -27,7 +27,13 @@ obj-$(CONFIG_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o | |||
| 27 | nvmem_rockchip_efuse-y := rockchip-efuse.o | 27 | nvmem_rockchip_efuse-y := rockchip-efuse.o |
| 28 | obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o | 28 | obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o |
| 29 | nvmem_sunxi_sid-y := sunxi_sid.o | 29 | nvmem_sunxi_sid-y := sunxi_sid.o |
| 30 | obj-$(CONFIG_UNIPHIER_EFUSE) += nvmem-uniphier-efuse.o | ||
| 31 | nvmem-uniphier-efuse-y := uniphier-efuse.o | ||
| 30 | obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o | 32 | obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o |
| 31 | nvmem-vf610-ocotp-y := vf610-ocotp.o | 33 | nvmem-vf610-ocotp-y := vf610-ocotp.o |
| 32 | obj-$(CONFIG_MESON_EFUSE) += nvmem_meson_efuse.o | 34 | obj-$(CONFIG_MESON_EFUSE) += nvmem_meson_efuse.o |
| 33 | nvmem_meson_efuse-y := meson-efuse.o | 35 | nvmem_meson_efuse-y := meson-efuse.o |
| 36 | obj-$(CONFIG_MESON_MX_EFUSE) += nvmem_meson_mx_efuse.o | ||
| 37 | nvmem_meson_mx_efuse-y := meson-mx-efuse.o | ||
| 38 | obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o | ||
| 39 | nvmem_snvs_lpgpr-y := snvs_lpgpr.o | ||
diff --git a/drivers/nvmem/bcm-ocotp.c b/drivers/nvmem/bcm-ocotp.c index 3c56e3b2bd65..5e9e324427f9 100644 --- a/drivers/nvmem/bcm-ocotp.c +++ b/drivers/nvmem/bcm-ocotp.c | |||
| @@ -232,7 +232,6 @@ static struct nvmem_config bcm_otpc_nvmem_config = { | |||
| 232 | .read_only = false, | 232 | .read_only = false, |
| 233 | .word_size = 4, | 233 | .word_size = 4, |
| 234 | .stride = 4, | 234 | .stride = 4, |
| 235 | .owner = THIS_MODULE, | ||
| 236 | .reg_read = bcm_otpc_read, | 235 | .reg_read = bcm_otpc_read, |
| 237 | .reg_write = bcm_otpc_write, | 236 | .reg_write = bcm_otpc_write, |
| 238 | }; | 237 | }; |
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index d12e5de78e70..5a5cefd12153 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c | |||
| @@ -462,6 +462,8 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) | |||
| 462 | 462 | ||
| 463 | nvmem->id = rval; | 463 | nvmem->id = rval; |
| 464 | nvmem->owner = config->owner; | 464 | nvmem->owner = config->owner; |
| 465 | if (!nvmem->owner && config->dev->driver) | ||
| 466 | nvmem->owner = config->dev->driver->owner; | ||
| 465 | nvmem->stride = config->stride; | 467 | nvmem->stride = config->stride; |
| 466 | nvmem->word_size = config->word_size; | 468 | nvmem->word_size = config->word_size; |
| 467 | nvmem->size = config->size; | 469 | nvmem->size = config->size; |
| @@ -615,7 +617,7 @@ static struct nvmem_device *nvmem_find(const char *name) | |||
| 615 | return to_nvmem_device(d); | 617 | return to_nvmem_device(d); |
| 616 | } | 618 | } |
| 617 | 619 | ||
| 618 | #if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) | 620 | #if IS_ENABLED(CONFIG_OF) |
| 619 | /** | 621 | /** |
| 620 | * of_nvmem_device_get() - Get nvmem device from a given id | 622 | * of_nvmem_device_get() - Get nvmem device from a given id |
| 621 | * | 623 | * |
| @@ -753,7 +755,7 @@ static struct nvmem_cell *nvmem_cell_get_from_list(const char *cell_id) | |||
| 753 | return cell; | 755 | return cell; |
| 754 | } | 756 | } |
| 755 | 757 | ||
| 756 | #if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) | 758 | #if IS_ENABLED(CONFIG_OF) |
| 757 | /** | 759 | /** |
| 758 | * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id | 760 | * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id |
| 759 | * | 761 | * |
| @@ -946,8 +948,7 @@ void nvmem_cell_put(struct nvmem_cell *cell) | |||
| 946 | } | 948 | } |
| 947 | EXPORT_SYMBOL_GPL(nvmem_cell_put); | 949 | EXPORT_SYMBOL_GPL(nvmem_cell_put); |
| 948 | 950 | ||
| 949 | static inline void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, | 951 | static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf) |
| 950 | void *buf) | ||
| 951 | { | 952 | { |
| 952 | u8 *p, *b; | 953 | u8 *p, *b; |
| 953 | int i, bit_offset = cell->bit_offset; | 954 | int i, bit_offset = cell->bit_offset; |
| @@ -1028,8 +1029,8 @@ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len) | |||
| 1028 | } | 1029 | } |
| 1029 | EXPORT_SYMBOL_GPL(nvmem_cell_read); | 1030 | EXPORT_SYMBOL_GPL(nvmem_cell_read); |
| 1030 | 1031 | ||
| 1031 | static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell, | 1032 | static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell, |
| 1032 | u8 *_buf, int len) | 1033 | u8 *_buf, int len) |
| 1033 | { | 1034 | { |
| 1034 | struct nvmem_device *nvmem = cell->nvmem; | 1035 | struct nvmem_device *nvmem = cell->nvmem; |
| 1035 | int i, rc, nbits, bit_offset = cell->bit_offset; | 1036 | int i, rc, nbits, bit_offset = cell->bit_offset; |
diff --git a/drivers/nvmem/imx-iim.c b/drivers/nvmem/imx-iim.c index 52ff65e0673f..52cfe91d9762 100644 --- a/drivers/nvmem/imx-iim.c +++ b/drivers/nvmem/imx-iim.c | |||
| @@ -34,7 +34,6 @@ struct imx_iim_drvdata { | |||
| 34 | struct iim_priv { | 34 | struct iim_priv { |
| 35 | void __iomem *base; | 35 | void __iomem *base; |
| 36 | struct clk *clk; | 36 | struct clk *clk; |
| 37 | struct nvmem_config nvmem; | ||
| 38 | }; | 37 | }; |
| 39 | 38 | ||
| 40 | static int imx_iim_read(void *context, unsigned int offset, | 39 | static int imx_iim_read(void *context, unsigned int offset, |
| @@ -108,7 +107,7 @@ static int imx_iim_probe(struct platform_device *pdev) | |||
| 108 | struct resource *res; | 107 | struct resource *res; |
| 109 | struct iim_priv *iim; | 108 | struct iim_priv *iim; |
| 110 | struct nvmem_device *nvmem; | 109 | struct nvmem_device *nvmem; |
| 111 | struct nvmem_config *cfg; | 110 | struct nvmem_config cfg = {}; |
| 112 | const struct imx_iim_drvdata *drvdata = NULL; | 111 | const struct imx_iim_drvdata *drvdata = NULL; |
| 113 | 112 | ||
| 114 | iim = devm_kzalloc(dev, sizeof(*iim), GFP_KERNEL); | 113 | iim = devm_kzalloc(dev, sizeof(*iim), GFP_KERNEL); |
| @@ -130,19 +129,16 @@ static int imx_iim_probe(struct platform_device *pdev) | |||
| 130 | if (IS_ERR(iim->clk)) | 129 | if (IS_ERR(iim->clk)) |
| 131 | return PTR_ERR(iim->clk); | 130 | return PTR_ERR(iim->clk); |
| 132 | 131 | ||
| 133 | cfg = &iim->nvmem; | 132 | cfg.name = "imx-iim", |
| 133 | cfg.read_only = true, | ||
| 134 | cfg.word_size = 1, | ||
| 135 | cfg.stride = 1, | ||
| 136 | cfg.reg_read = imx_iim_read, | ||
| 137 | cfg.dev = dev; | ||
| 138 | cfg.size = drvdata->nregs; | ||
| 139 | cfg.priv = iim; | ||
| 134 | 140 | ||
| 135 | cfg->name = "imx-iim", | 141 | nvmem = nvmem_register(&cfg); |
| 136 | cfg->read_only = true, | ||
| 137 | cfg->word_size = 1, | ||
| 138 | cfg->stride = 1, | ||
| 139 | cfg->owner = THIS_MODULE, | ||
| 140 | cfg->reg_read = imx_iim_read, | ||
| 141 | cfg->dev = dev; | ||
| 142 | cfg->size = drvdata->nregs; | ||
| 143 | cfg->priv = iim; | ||
| 144 | |||
| 145 | nvmem = nvmem_register(cfg); | ||
| 146 | if (IS_ERR(nvmem)) | 142 | if (IS_ERR(nvmem)) |
| 147 | return PTR_ERR(nvmem); | 143 | return PTR_ERR(nvmem); |
| 148 | 144 | ||
diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index 193ca8fd350a..d7ba351a70c9 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c | |||
| @@ -40,14 +40,19 @@ | |||
| 40 | #define IMX_OCOTP_ADDR_CTRL_SET 0x0004 | 40 | #define IMX_OCOTP_ADDR_CTRL_SET 0x0004 |
| 41 | #define IMX_OCOTP_ADDR_CTRL_CLR 0x0008 | 41 | #define IMX_OCOTP_ADDR_CTRL_CLR 0x0008 |
| 42 | #define IMX_OCOTP_ADDR_TIMING 0x0010 | 42 | #define IMX_OCOTP_ADDR_TIMING 0x0010 |
| 43 | #define IMX_OCOTP_ADDR_DATA 0x0020 | 43 | #define IMX_OCOTP_ADDR_DATA0 0x0020 |
| 44 | #define IMX_OCOTP_ADDR_DATA1 0x0030 | ||
| 45 | #define IMX_OCOTP_ADDR_DATA2 0x0040 | ||
| 46 | #define IMX_OCOTP_ADDR_DATA3 0x0050 | ||
| 44 | 47 | ||
| 45 | #define IMX_OCOTP_BM_CTRL_ADDR 0x0000007F | 48 | #define IMX_OCOTP_BM_CTRL_ADDR 0x0000007F |
| 46 | #define IMX_OCOTP_BM_CTRL_BUSY 0x00000100 | 49 | #define IMX_OCOTP_BM_CTRL_BUSY 0x00000100 |
| 47 | #define IMX_OCOTP_BM_CTRL_ERROR 0x00000200 | 50 | #define IMX_OCOTP_BM_CTRL_ERROR 0x00000200 |
| 48 | #define IMX_OCOTP_BM_CTRL_REL_SHADOWS 0x00000400 | 51 | #define IMX_OCOTP_BM_CTRL_REL_SHADOWS 0x00000400 |
| 49 | 52 | ||
| 50 | #define DEF_RELAX 20 /* > 16.5ns */ | 53 | #define DEF_RELAX 20 /* > 16.5ns */ |
| 54 | #define DEF_FSOURCE 1001 /* > 1000 ns */ | ||
| 55 | #define DEF_STROBE_PROG 10000 /* IPG clocks */ | ||
| 51 | #define IMX_OCOTP_WR_UNLOCK 0x3E770000 | 56 | #define IMX_OCOTP_WR_UNLOCK 0x3E770000 |
| 52 | #define IMX_OCOTP_READ_LOCKED_VAL 0xBADABADA | 57 | #define IMX_OCOTP_READ_LOCKED_VAL 0xBADABADA |
| 53 | 58 | ||
| @@ -57,10 +62,16 @@ struct ocotp_priv { | |||
| 57 | struct device *dev; | 62 | struct device *dev; |
| 58 | struct clk *clk; | 63 | struct clk *clk; |
| 59 | void __iomem *base; | 64 | void __iomem *base; |
| 60 | unsigned int nregs; | 65 | const struct ocotp_params *params; |
| 61 | struct nvmem_config *config; | 66 | struct nvmem_config *config; |
| 62 | }; | 67 | }; |
| 63 | 68 | ||
| 69 | struct ocotp_params { | ||
| 70 | unsigned int nregs; | ||
| 71 | unsigned int bank_address_words; | ||
| 72 | void (*set_timing)(struct ocotp_priv *priv); | ||
| 73 | }; | ||
| 74 | |||
| 64 | static int imx_ocotp_wait_for_busy(void __iomem *base, u32 flags) | 75 | static int imx_ocotp_wait_for_busy(void __iomem *base, u32 flags) |
| 65 | { | 76 | { |
| 66 | int count; | 77 | int count; |
| @@ -121,8 +132,8 @@ static int imx_ocotp_read(void *context, unsigned int offset, | |||
| 121 | index = offset >> 2; | 132 | index = offset >> 2; |
| 122 | count = bytes >> 2; | 133 | count = bytes >> 2; |
| 123 | 134 | ||
| 124 | if (count > (priv->nregs - index)) | 135 | if (count > (priv->params->nregs - index)) |
| 125 | count = priv->nregs - index; | 136 | count = priv->params->nregs - index; |
| 126 | 137 | ||
| 127 | mutex_lock(&ocotp_mutex); | 138 | mutex_lock(&ocotp_mutex); |
| 128 | 139 | ||
| @@ -160,6 +171,52 @@ read_end: | |||
| 160 | return ret; | 171 | return ret; |
| 161 | } | 172 | } |
| 162 | 173 | ||
| 174 | static void imx_ocotp_set_imx6_timing(struct ocotp_priv *priv) | ||
| 175 | { | ||
| 176 | unsigned long clk_rate = 0; | ||
| 177 | unsigned long strobe_read, relax, strobe_prog; | ||
| 178 | u32 timing = 0; | ||
| 179 | |||
| 180 | /* 47.3.1.3.1 | ||
| 181 | * Program HW_OCOTP_TIMING[STROBE_PROG] and HW_OCOTP_TIMING[RELAX] | ||
| 182 | * fields with timing values to match the current frequency of the | ||
| 183 | * ipg_clk. OTP writes will work at maximum bus frequencies as long | ||
| 184 | * as the HW_OCOTP_TIMING parameters are set correctly. | ||
| 185 | */ | ||
| 186 | clk_rate = clk_get_rate(priv->clk); | ||
| 187 | |||
| 188 | relax = clk_rate / (1000000000 / DEF_RELAX) - 1; | ||
| 189 | strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1; | ||
| 190 | strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1; | ||
| 191 | |||
| 192 | timing = strobe_prog & 0x00000FFF; | ||
| 193 | timing |= (relax << 12) & 0x0000F000; | ||
| 194 | timing |= (strobe_read << 16) & 0x003F0000; | ||
| 195 | |||
| 196 | writel(timing, priv->base + IMX_OCOTP_ADDR_TIMING); | ||
| 197 | } | ||
| 198 | |||
| 199 | static void imx_ocotp_set_imx7_timing(struct ocotp_priv *priv) | ||
| 200 | { | ||
| 201 | unsigned long clk_rate = 0; | ||
| 202 | u64 fsource, strobe_prog; | ||
| 203 | u32 timing = 0; | ||
| 204 | |||
| 205 | /* i.MX 7Solo Applications Processor Reference Manual, Rev. 0.1 | ||
| 206 | * 6.4.3.3 | ||
| 207 | */ | ||
| 208 | clk_rate = clk_get_rate(priv->clk); | ||
| 209 | fsource = DIV_ROUND_UP_ULL((u64)clk_rate * DEF_FSOURCE, | ||
| 210 | NSEC_PER_SEC) + 1; | ||
| 211 | strobe_prog = DIV_ROUND_CLOSEST_ULL((u64)clk_rate * DEF_STROBE_PROG, | ||
| 212 | NSEC_PER_SEC) + 1; | ||
| 213 | |||
| 214 | timing = strobe_prog & 0x00000FFF; | ||
| 215 | timing |= (fsource << 12) & 0x000FF000; | ||
| 216 | |||
| 217 | writel(timing, priv->base + IMX_OCOTP_ADDR_TIMING); | ||
| 218 | } | ||
| 219 | |||
| 163 | static int imx_ocotp_write(void *context, unsigned int offset, void *val, | 220 | static int imx_ocotp_write(void *context, unsigned int offset, void *val, |
| 164 | size_t bytes) | 221 | size_t bytes) |
| 165 | { | 222 | { |
| @@ -167,11 +224,9 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val, | |||
| 167 | u32 *buf = val; | 224 | u32 *buf = val; |
| 168 | int ret; | 225 | int ret; |
| 169 | 226 | ||
| 170 | unsigned long clk_rate = 0; | ||
| 171 | unsigned long strobe_read, relax, strobe_prog; | ||
| 172 | u32 timing = 0; | ||
| 173 | u32 ctrl; | 227 | u32 ctrl; |
| 174 | u8 waddr; | 228 | u8 waddr; |
| 229 | u8 word = 0; | ||
| 175 | 230 | ||
| 176 | /* allow only writing one complete OTP word at a time */ | 231 | /* allow only writing one complete OTP word at a time */ |
| 177 | if ((bytes != priv->config->word_size) || | 232 | if ((bytes != priv->config->word_size) || |
| @@ -187,23 +242,8 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val, | |||
| 187 | return ret; | 242 | return ret; |
| 188 | } | 243 | } |
| 189 | 244 | ||
| 190 | /* 47.3.1.3.1 | 245 | /* Setup the write timing values */ |
| 191 | * Program HW_OCOTP_TIMING[STROBE_PROG] and HW_OCOTP_TIMING[RELAX] | 246 | priv->params->set_timing(priv); |
| 192 | * fields with timing values to match the current frequency of the | ||
| 193 | * ipg_clk. OTP writes will work at maximum bus frequencies as long | ||
| 194 | * as the HW_OCOTP_TIMING parameters are set correctly. | ||
| 195 | */ | ||
| 196 | clk_rate = clk_get_rate(priv->clk); | ||
| 197 | |||
| 198 | relax = clk_rate / (1000000000 / DEF_RELAX) - 1; | ||
| 199 | strobe_prog = clk_rate / (1000000000 / 10000) + 2 * (DEF_RELAX + 1) - 1; | ||
| 200 | strobe_read = clk_rate / (1000000000 / 40) + 2 * (DEF_RELAX + 1) - 1; | ||
| 201 | |||
| 202 | timing = strobe_prog & 0x00000FFF; | ||
| 203 | timing |= (relax << 12) & 0x0000F000; | ||
| 204 | timing |= (strobe_read << 16) & 0x003F0000; | ||
| 205 | |||
| 206 | writel(timing, priv->base + IMX_OCOTP_ADDR_TIMING); | ||
| 207 | 247 | ||
| 208 | /* 47.3.1.3.2 | 248 | /* 47.3.1.3.2 |
| 209 | * Check that HW_OCOTP_CTRL[BUSY] and HW_OCOTP_CTRL[ERROR] are clear. | 249 | * 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, | |||
| 224 | * description. Both the unlock code and address can be written in the | 264 | * description. Both the unlock code and address can be written in the |
| 225 | * same operation. | 265 | * same operation. |
| 226 | */ | 266 | */ |
| 227 | /* OTP write/read address specifies one of 128 word address locations */ | 267 | if (priv->params->bank_address_words != 0) { |
| 228 | waddr = offset / 4; | 268 | /* |
| 269 | * In banked/i.MX7 mode the OTP register bank goes into waddr | ||
| 270 | * see i.MX 7Solo Applications Processor Reference Manual, Rev. | ||
| 271 | * 0.1 section 6.4.3.1 | ||
| 272 | */ | ||
| 273 | offset = offset / priv->config->word_size; | ||
| 274 | waddr = offset / priv->params->bank_address_words; | ||
| 275 | word = offset & (priv->params->bank_address_words - 1); | ||
| 276 | } else { | ||
| 277 | /* | ||
| 278 | * Non-banked i.MX6 mode. | ||
| 279 | * OTP write/read address specifies one of 128 word address | ||
| 280 | * locations | ||
| 281 | */ | ||
| 282 | waddr = offset / 4; | ||
| 283 | } | ||
| 229 | 284 | ||
| 230 | ctrl = readl(priv->base + IMX_OCOTP_ADDR_CTRL); | 285 | ctrl = readl(priv->base + IMX_OCOTP_ADDR_CTRL); |
| 231 | ctrl &= ~IMX_OCOTP_BM_CTRL_ADDR; | 286 | ctrl &= ~IMX_OCOTP_BM_CTRL_ADDR; |
| @@ -251,8 +306,43 @@ static int imx_ocotp_write(void *context, unsigned int offset, void *val, | |||
| 251 | * shift right (with zero fill). This shifting is required to program | 306 | * shift right (with zero fill). This shifting is required to program |
| 252 | * the OTP serially. During the write operation, HW_OCOTP_DATA cannot be | 307 | * the OTP serially. During the write operation, HW_OCOTP_DATA cannot be |
| 253 | * modified. | 308 | * modified. |
| 309 | * Note: on i.MX7 there are four data fields to write for banked write | ||
| 310 | * with the fuse blowing operation only taking place after data0 | ||
| 311 | * has been written. This is why data0 must always be the last | ||
| 312 | * register written. | ||
| 254 | */ | 313 | */ |
| 255 | writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA); | 314 | if (priv->params->bank_address_words != 0) { |
| 315 | /* Banked/i.MX7 mode */ | ||
| 316 | switch (word) { | ||
| 317 | case 0: | ||
| 318 | writel(0, priv->base + IMX_OCOTP_ADDR_DATA1); | ||
| 319 | writel(0, priv->base + IMX_OCOTP_ADDR_DATA2); | ||
| 320 | writel(0, priv->base + IMX_OCOTP_ADDR_DATA3); | ||
| 321 | writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA0); | ||
| 322 | break; | ||
| 323 | case 1: | ||
| 324 | writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA1); | ||
| 325 | writel(0, priv->base + IMX_OCOTP_ADDR_DATA2); | ||
| 326 | writel(0, priv->base + IMX_OCOTP_ADDR_DATA3); | ||
| 327 | writel(0, priv->base + IMX_OCOTP_ADDR_DATA0); | ||
| 328 | break; | ||
| 329 | case 2: | ||
| 330 | writel(0, priv->base + IMX_OCOTP_ADDR_DATA1); | ||
| 331 | writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA2); | ||
| 332 | writel(0, priv->base + IMX_OCOTP_ADDR_DATA3); | ||
| 333 | writel(0, priv->base + IMX_OCOTP_ADDR_DATA0); | ||
| 334 | break; | ||
| 335 | case 3: | ||
| 336 | writel(0, priv->base + IMX_OCOTP_ADDR_DATA1); | ||
| 337 | writel(0, priv->base + IMX_OCOTP_ADDR_DATA2); | ||
| 338 | writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA3); | ||
| 339 | writel(0, priv->base + IMX_OCOTP_ADDR_DATA0); | ||
| 340 | break; | ||
| 341 | } | ||
| 342 | } else { | ||
| 343 | /* Non-banked i.MX6 mode */ | ||
| 344 | writel(*buf, priv->base + IMX_OCOTP_ADDR_DATA0); | ||
| 345 | } | ||
| 256 | 346 | ||
| 257 | /* 47.4.1.4.5 | 347 | /* 47.4.1.4.5 |
| 258 | * Once complete, the controller will clear BUSY. A write request to a | 348 | * Once complete, the controller will clear BUSY. A write request to a |
| @@ -303,17 +393,46 @@ static struct nvmem_config imx_ocotp_nvmem_config = { | |||
| 303 | .read_only = false, | 393 | .read_only = false, |
| 304 | .word_size = 4, | 394 | .word_size = 4, |
| 305 | .stride = 4, | 395 | .stride = 4, |
| 306 | .owner = THIS_MODULE, | ||
| 307 | .reg_read = imx_ocotp_read, | 396 | .reg_read = imx_ocotp_read, |
| 308 | .reg_write = imx_ocotp_write, | 397 | .reg_write = imx_ocotp_write, |
| 309 | }; | 398 | }; |
| 310 | 399 | ||
| 400 | static const struct ocotp_params imx6q_params = { | ||
| 401 | .nregs = 128, | ||
| 402 | .bank_address_words = 0, | ||
| 403 | .set_timing = imx_ocotp_set_imx6_timing, | ||
| 404 | }; | ||
| 405 | |||
| 406 | static const struct ocotp_params imx6sl_params = { | ||
| 407 | .nregs = 64, | ||
| 408 | .bank_address_words = 0, | ||
| 409 | .set_timing = imx_ocotp_set_imx6_timing, | ||
| 410 | }; | ||
| 411 | |||
| 412 | static const struct ocotp_params imx6sx_params = { | ||
| 413 | .nregs = 128, | ||
| 414 | .bank_address_words = 0, | ||
| 415 | .set_timing = imx_ocotp_set_imx6_timing, | ||
| 416 | }; | ||
| 417 | |||
| 418 | static const struct ocotp_params imx6ul_params = { | ||
| 419 | .nregs = 128, | ||
| 420 | .bank_address_words = 0, | ||
| 421 | .set_timing = imx_ocotp_set_imx6_timing, | ||
| 422 | }; | ||
| 423 | |||
| 424 | static const struct ocotp_params imx7d_params = { | ||
| 425 | .nregs = 64, | ||
| 426 | .bank_address_words = 4, | ||
| 427 | .set_timing = imx_ocotp_set_imx7_timing, | ||
| 428 | }; | ||
| 429 | |||
| 311 | static const struct of_device_id imx_ocotp_dt_ids[] = { | 430 | static const struct of_device_id imx_ocotp_dt_ids[] = { |
| 312 | { .compatible = "fsl,imx6q-ocotp", (void *)128 }, | 431 | { .compatible = "fsl,imx6q-ocotp", .data = &imx6q_params }, |
| 313 | { .compatible = "fsl,imx6sl-ocotp", (void *)64 }, | 432 | { .compatible = "fsl,imx6sl-ocotp", .data = &imx6sl_params }, |
| 314 | { .compatible = "fsl,imx6sx-ocotp", (void *)128 }, | 433 | { .compatible = "fsl,imx6sx-ocotp", .data = &imx6sx_params }, |
| 315 | { .compatible = "fsl,imx6ul-ocotp", (void *)128 }, | 434 | { .compatible = "fsl,imx6ul-ocotp", .data = &imx6ul_params }, |
| 316 | { .compatible = "fsl,imx7d-ocotp", (void *)64 }, | 435 | { .compatible = "fsl,imx7d-ocotp", .data = &imx7d_params }, |
| 317 | { }, | 436 | { }, |
| 318 | }; | 437 | }; |
| 319 | MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); | 438 | MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids); |
| @@ -342,8 +461,8 @@ static int imx_ocotp_probe(struct platform_device *pdev) | |||
| 342 | return PTR_ERR(priv->clk); | 461 | return PTR_ERR(priv->clk); |
| 343 | 462 | ||
| 344 | of_id = of_match_device(imx_ocotp_dt_ids, dev); | 463 | of_id = of_match_device(imx_ocotp_dt_ids, dev); |
| 345 | priv->nregs = (unsigned long)of_id->data; | 464 | priv->params = of_device_get_match_data(&pdev->dev); |
| 346 | imx_ocotp_nvmem_config.size = 4 * priv->nregs; | 465 | imx_ocotp_nvmem_config.size = 4 * priv->params->nregs; |
| 347 | imx_ocotp_nvmem_config.dev = dev; | 466 | imx_ocotp_nvmem_config.dev = dev; |
| 348 | imx_ocotp_nvmem_config.priv = priv; | 467 | imx_ocotp_nvmem_config.priv = priv; |
| 349 | priv->config = &imx_ocotp_nvmem_config; | 468 | priv->config = &imx_ocotp_nvmem_config; |
| @@ -375,5 +494,5 @@ static struct platform_driver imx_ocotp_driver = { | |||
| 375 | module_platform_driver(imx_ocotp_driver); | 494 | module_platform_driver(imx_ocotp_driver); |
| 376 | 495 | ||
| 377 | MODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>"); | 496 | MODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>"); |
| 378 | MODULE_DESCRIPTION("i.MX6 OCOTP fuse box driver"); | 497 | MODULE_DESCRIPTION("i.MX6/i.MX7 OCOTP fuse box driver"); |
| 379 | MODULE_LICENSE("GPL v2"); | 498 | MODULE_LICENSE("GPL v2"); |
diff --git a/drivers/nvmem/lpc18xx_eeprom.c b/drivers/nvmem/lpc18xx_eeprom.c index 6c7e2c424a4e..b1af966206a6 100644 --- a/drivers/nvmem/lpc18xx_eeprom.c +++ b/drivers/nvmem/lpc18xx_eeprom.c | |||
| @@ -159,7 +159,6 @@ static struct nvmem_config lpc18xx_nvmem_config = { | |||
| 159 | .word_size = 4, | 159 | .word_size = 4, |
| 160 | .reg_read = lpc18xx_eeprom_read, | 160 | .reg_read = lpc18xx_eeprom_read, |
| 161 | .reg_write = lpc18xx_eeprom_gather_write, | 161 | .reg_write = lpc18xx_eeprom_gather_write, |
| 162 | .owner = THIS_MODULE, | ||
| 163 | }; | 162 | }; |
| 164 | 163 | ||
| 165 | static int lpc18xx_eeprom_probe(struct platform_device *pdev) | 164 | static int lpc18xx_eeprom_probe(struct platform_device *pdev) |
diff --git a/drivers/nvmem/lpc18xx_otp.c b/drivers/nvmem/lpc18xx_otp.c index be8d07403ffc..95268db155e9 100644 --- a/drivers/nvmem/lpc18xx_otp.c +++ b/drivers/nvmem/lpc18xx_otp.c | |||
| @@ -64,7 +64,6 @@ static struct nvmem_config lpc18xx_otp_nvmem_config = { | |||
| 64 | .read_only = true, | 64 | .read_only = true, |
| 65 | .word_size = LPC18XX_OTP_WORD_SIZE, | 65 | .word_size = LPC18XX_OTP_WORD_SIZE, |
| 66 | .stride = LPC18XX_OTP_WORD_SIZE, | 66 | .stride = LPC18XX_OTP_WORD_SIZE, |
| 67 | .owner = THIS_MODULE, | ||
| 68 | .reg_read = lpc18xx_otp_read, | 67 | .reg_read = lpc18xx_otp_read, |
| 69 | }; | 68 | }; |
| 70 | 69 | ||
diff --git a/drivers/nvmem/meson-efuse.c b/drivers/nvmem/meson-efuse.c index 70bfc9839bb2..a43c68f90937 100644 --- a/drivers/nvmem/meson-efuse.c +++ b/drivers/nvmem/meson-efuse.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Amlogic eFuse Driver | 2 | * Amlogic Meson GX eFuse Driver |
| 3 | * | 3 | * |
| 4 | * Copyright (c) 2016 Endless Computers, Inc. | 4 | * Copyright (c) 2016 Endless Computers, Inc. |
| 5 | * Author: Carlo Caione <carlo@endlessm.com> | 5 | * Author: Carlo Caione <carlo@endlessm.com> |
| @@ -37,7 +37,6 @@ static int meson_efuse_read(void *context, unsigned int offset, | |||
| 37 | 37 | ||
| 38 | static struct nvmem_config econfig = { | 38 | static struct nvmem_config econfig = { |
| 39 | .name = "meson-efuse", | 39 | .name = "meson-efuse", |
| 40 | .owner = THIS_MODULE, | ||
| 41 | .stride = 1, | 40 | .stride = 1, |
| 42 | .word_size = 1, | 41 | .word_size = 1, |
| 43 | .read_only = true, | 42 | .read_only = true, |
| @@ -89,5 +88,5 @@ static struct platform_driver meson_efuse_driver = { | |||
| 89 | module_platform_driver(meson_efuse_driver); | 88 | module_platform_driver(meson_efuse_driver); |
| 90 | 89 | ||
| 91 | MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>"); | 90 | MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>"); |
| 92 | MODULE_DESCRIPTION("Amlogic Meson NVMEM driver"); | 91 | MODULE_DESCRIPTION("Amlogic Meson GX NVMEM driver"); |
| 93 | MODULE_LICENSE("GPL v2"); | 92 | MODULE_LICENSE("GPL v2"); |
diff --git a/drivers/nvmem/meson-mx-efuse.c b/drivers/nvmem/meson-mx-efuse.c new file mode 100644 index 000000000000..a346b4923550 --- /dev/null +++ b/drivers/nvmem/meson-mx-efuse.c | |||
| @@ -0,0 +1,265 @@ | |||
| 1 | /* | ||
| 2 | * Amlogic Meson6, Meson8 and Meson8b eFuse Driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of version 2 of the GNU General Public License as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 13 | * more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/bitfield.h> | ||
| 17 | #include <linux/bitops.h> | ||
| 18 | #include <linux/clk.h> | ||
| 19 | #include <linux/delay.h> | ||
| 20 | #include <linux/io.h> | ||
| 21 | #include <linux/iopoll.h> | ||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/nvmem-provider.h> | ||
| 24 | #include <linux/of.h> | ||
| 25 | #include <linux/of_device.h> | ||
| 26 | #include <linux/platform_device.h> | ||
| 27 | #include <linux/sizes.h> | ||
| 28 | #include <linux/slab.h> | ||
| 29 | |||
| 30 | #define MESON_MX_EFUSE_CNTL1 0x04 | ||
| 31 | #define MESON_MX_EFUSE_CNTL1_PD_ENABLE BIT(27) | ||
| 32 | #define MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY BIT(26) | ||
| 33 | #define MESON_MX_EFUSE_CNTL1_AUTO_RD_START BIT(25) | ||
| 34 | #define MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE BIT(24) | ||
| 35 | #define MESON_MX_EFUSE_CNTL1_BYTE_WR_DATA GENMASK(23, 16) | ||
| 36 | #define MESON_MX_EFUSE_CNTL1_AUTO_WR_BUSY BIT(14) | ||
| 37 | #define MESON_MX_EFUSE_CNTL1_AUTO_WR_START BIT(13) | ||
| 38 | #define MESON_MX_EFUSE_CNTL1_AUTO_WR_ENABLE BIT(12) | ||
| 39 | #define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET BIT(11) | ||
| 40 | #define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK GENMASK(10, 0) | ||
| 41 | |||
| 42 | #define MESON_MX_EFUSE_CNTL2 0x08 | ||
| 43 | |||
| 44 | #define MESON_MX_EFUSE_CNTL4 0x10 | ||
| 45 | #define MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE BIT(10) | ||
| 46 | |||
| 47 | struct meson_mx_efuse_platform_data { | ||
| 48 | const char *name; | ||
| 49 | unsigned int word_size; | ||
| 50 | }; | ||
| 51 | |||
| 52 | struct meson_mx_efuse { | ||
| 53 | void __iomem *base; | ||
| 54 | struct clk *core_clk; | ||
| 55 | struct nvmem_device *nvmem; | ||
| 56 | struct nvmem_config config; | ||
| 57 | }; | ||
| 58 | |||
| 59 | static void meson_mx_efuse_mask_bits(struct meson_mx_efuse *efuse, u32 reg, | ||
| 60 | u32 mask, u32 set) | ||
| 61 | { | ||
| 62 | u32 data; | ||
| 63 | |||
| 64 | data = readl(efuse->base + reg); | ||
| 65 | data &= ~mask; | ||
| 66 | data |= (set & mask); | ||
| 67 | |||
| 68 | writel(data, efuse->base + reg); | ||
| 69 | } | ||
| 70 | |||
| 71 | static int meson_mx_efuse_hw_enable(struct meson_mx_efuse *efuse) | ||
| 72 | { | ||
| 73 | int err; | ||
| 74 | |||
| 75 | err = clk_prepare_enable(efuse->core_clk); | ||
| 76 | if (err) | ||
| 77 | return err; | ||
| 78 | |||
| 79 | /* power up the efuse */ | ||
| 80 | meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, | ||
| 81 | MESON_MX_EFUSE_CNTL1_PD_ENABLE, 0); | ||
| 82 | |||
| 83 | meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL4, | ||
| 84 | MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE, 0); | ||
| 85 | |||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | static void meson_mx_efuse_hw_disable(struct meson_mx_efuse *efuse) | ||
| 90 | { | ||
| 91 | meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, | ||
| 92 | MESON_MX_EFUSE_CNTL1_PD_ENABLE, | ||
| 93 | MESON_MX_EFUSE_CNTL1_PD_ENABLE); | ||
| 94 | |||
| 95 | clk_disable_unprepare(efuse->core_clk); | ||
| 96 | } | ||
| 97 | |||
| 98 | static int meson_mx_efuse_read_addr(struct meson_mx_efuse *efuse, | ||
| 99 | unsigned int addr, u32 *value) | ||
| 100 | { | ||
| 101 | int err; | ||
| 102 | u32 regval; | ||
| 103 | |||
| 104 | /* write the address to read */ | ||
| 105 | regval = FIELD_PREP(MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, addr); | ||
| 106 | meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, | ||
| 107 | MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, regval); | ||
| 108 | |||
| 109 | /* inform the hardware that we changed the address */ | ||
| 110 | meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, | ||
| 111 | MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET, | ||
| 112 | MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET); | ||
| 113 | meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, | ||
| 114 | MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET, 0); | ||
| 115 | |||
| 116 | /* start the read process */ | ||
| 117 | meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, | ||
| 118 | MESON_MX_EFUSE_CNTL1_AUTO_RD_START, | ||
| 119 | MESON_MX_EFUSE_CNTL1_AUTO_RD_START); | ||
| 120 | meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, | ||
| 121 | MESON_MX_EFUSE_CNTL1_AUTO_RD_START, 0); | ||
| 122 | |||
| 123 | /* | ||
| 124 | * perform a dummy read to ensure that the HW has the RD_BUSY bit set | ||
| 125 | * when polling for the status below. | ||
| 126 | */ | ||
| 127 | readl(efuse->base + MESON_MX_EFUSE_CNTL1); | ||
| 128 | |||
| 129 | err = readl_poll_timeout_atomic(efuse->base + MESON_MX_EFUSE_CNTL1, | ||
| 130 | regval, | ||
| 131 | (!(regval & MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY)), | ||
| 132 | 1, 1000); | ||
| 133 | if (err) { | ||
| 134 | dev_err(efuse->config.dev, | ||
| 135 | "Timeout while reading efuse address %u\n", addr); | ||
| 136 | return err; | ||
| 137 | } | ||
| 138 | |||
| 139 | *value = readl(efuse->base + MESON_MX_EFUSE_CNTL2); | ||
| 140 | |||
| 141 | return 0; | ||
| 142 | } | ||
| 143 | |||
| 144 | static int meson_mx_efuse_read(void *context, unsigned int offset, | ||
| 145 | void *buf, size_t bytes) | ||
| 146 | { | ||
| 147 | struct meson_mx_efuse *efuse = context; | ||
| 148 | u32 tmp; | ||
| 149 | int err, i, addr; | ||
| 150 | |||
| 151 | err = meson_mx_efuse_hw_enable(efuse); | ||
| 152 | if (err) | ||
| 153 | return err; | ||
| 154 | |||
| 155 | meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, | ||
| 156 | MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE, | ||
| 157 | MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE); | ||
| 158 | |||
| 159 | for (i = offset; i < offset + bytes; i += efuse->config.word_size) { | ||
| 160 | addr = i / efuse->config.word_size; | ||
| 161 | |||
| 162 | err = meson_mx_efuse_read_addr(efuse, addr, &tmp); | ||
| 163 | if (err) | ||
| 164 | break; | ||
| 165 | |||
| 166 | memcpy(buf + i, &tmp, efuse->config.word_size); | ||
| 167 | } | ||
| 168 | |||
| 169 | meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1, | ||
| 170 | MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE, 0); | ||
| 171 | |||
| 172 | meson_mx_efuse_hw_disable(efuse); | ||
| 173 | |||
| 174 | return err; | ||
| 175 | } | ||
| 176 | |||
| 177 | static const struct meson_mx_efuse_platform_data meson6_efuse_data = { | ||
| 178 | .name = "meson6-efuse", | ||
| 179 | .word_size = 1, | ||
| 180 | }; | ||
| 181 | |||
| 182 | static const struct meson_mx_efuse_platform_data meson8_efuse_data = { | ||
| 183 | .name = "meson8-efuse", | ||
| 184 | .word_size = 4, | ||
| 185 | }; | ||
| 186 | |||
| 187 | static const struct meson_mx_efuse_platform_data meson8b_efuse_data = { | ||
| 188 | .name = "meson8b-efuse", | ||
| 189 | .word_size = 4, | ||
| 190 | }; | ||
| 191 | |||
| 192 | static const struct of_device_id meson_mx_efuse_match[] = { | ||
| 193 | { .compatible = "amlogic,meson6-efuse", .data = &meson6_efuse_data }, | ||
| 194 | { .compatible = "amlogic,meson8-efuse", .data = &meson8_efuse_data }, | ||
| 195 | { .compatible = "amlogic,meson8b-efuse", .data = &meson8b_efuse_data }, | ||
| 196 | { /* sentinel */ }, | ||
| 197 | }; | ||
| 198 | MODULE_DEVICE_TABLE(of, meson_mx_efuse_match); | ||
| 199 | |||
| 200 | static int meson_mx_efuse_probe(struct platform_device *pdev) | ||
| 201 | { | ||
| 202 | const struct meson_mx_efuse_platform_data *drvdata; | ||
| 203 | struct meson_mx_efuse *efuse; | ||
| 204 | struct resource *res; | ||
| 205 | |||
| 206 | drvdata = of_device_get_match_data(&pdev->dev); | ||
| 207 | if (!drvdata) | ||
| 208 | return -EINVAL; | ||
| 209 | |||
| 210 | efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL); | ||
| 211 | if (!efuse) | ||
| 212 | return -ENOMEM; | ||
| 213 | |||
| 214 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 215 | efuse->base = devm_ioremap_resource(&pdev->dev, res); | ||
| 216 | if (IS_ERR(efuse->base)) | ||
| 217 | return PTR_ERR(efuse->base); | ||
| 218 | |||
| 219 | efuse->config.name = devm_kstrdup(&pdev->dev, drvdata->name, | ||
| 220 | GFP_KERNEL); | ||
| 221 | efuse->config.owner = THIS_MODULE; | ||
| 222 | efuse->config.dev = &pdev->dev; | ||
| 223 | efuse->config.priv = efuse; | ||
| 224 | efuse->config.stride = drvdata->word_size; | ||
| 225 | efuse->config.word_size = drvdata->word_size; | ||
| 226 | efuse->config.size = SZ_512; | ||
| 227 | efuse->config.read_only = true; | ||
| 228 | efuse->config.reg_read = meson_mx_efuse_read; | ||
| 229 | |||
| 230 | efuse->core_clk = devm_clk_get(&pdev->dev, "core"); | ||
| 231 | if (IS_ERR(efuse->core_clk)) { | ||
| 232 | dev_err(&pdev->dev, "Failed to get core clock\n"); | ||
| 233 | return PTR_ERR(efuse->core_clk); | ||
| 234 | } | ||
| 235 | |||
| 236 | efuse->nvmem = nvmem_register(&efuse->config); | ||
| 237 | if (IS_ERR(efuse->nvmem)) | ||
| 238 | return PTR_ERR(efuse->nvmem); | ||
| 239 | |||
| 240 | platform_set_drvdata(pdev, efuse); | ||
| 241 | |||
| 242 | return 0; | ||
| 243 | } | ||
| 244 | |||
| 245 | static int meson_mx_efuse_remove(struct platform_device *pdev) | ||
| 246 | { | ||
| 247 | struct meson_mx_efuse *efuse = platform_get_drvdata(pdev); | ||
| 248 | |||
| 249 | return nvmem_unregister(efuse->nvmem); | ||
| 250 | } | ||
| 251 | |||
| 252 | static struct platform_driver meson_mx_efuse_driver = { | ||
| 253 | .probe = meson_mx_efuse_probe, | ||
| 254 | .remove = meson_mx_efuse_remove, | ||
| 255 | .driver = { | ||
| 256 | .name = "meson-mx-efuse", | ||
| 257 | .of_match_table = meson_mx_efuse_match, | ||
| 258 | }, | ||
| 259 | }; | ||
| 260 | |||
| 261 | module_platform_driver(meson_mx_efuse_driver); | ||
| 262 | |||
| 263 | MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); | ||
| 264 | MODULE_DESCRIPTION("Amlogic Meson MX eFuse NVMEM driver"); | ||
| 265 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/nvmem/mtk-efuse.c b/drivers/nvmem/mtk-efuse.c index 32fd572e18c5..9ee3479cfc7b 100644 --- a/drivers/nvmem/mtk-efuse.c +++ b/drivers/nvmem/mtk-efuse.c | |||
| @@ -18,15 +18,19 @@ | |||
| 18 | #include <linux/nvmem-provider.h> | 18 | #include <linux/nvmem-provider.h> |
| 19 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
| 20 | 20 | ||
| 21 | struct mtk_efuse_priv { | ||
| 22 | void __iomem *base; | ||
| 23 | }; | ||
| 24 | |||
| 21 | static int mtk_reg_read(void *context, | 25 | static int mtk_reg_read(void *context, |
| 22 | unsigned int reg, void *_val, size_t bytes) | 26 | unsigned int reg, void *_val, size_t bytes) |
| 23 | { | 27 | { |
| 24 | void __iomem *base = context; | 28 | struct mtk_efuse_priv *priv = context; |
| 25 | u32 *val = _val; | 29 | u32 *val = _val; |
| 26 | int i = 0, words = bytes / 4; | 30 | int i = 0, words = bytes / 4; |
| 27 | 31 | ||
| 28 | while (words--) | 32 | while (words--) |
| 29 | *val++ = readl(base + reg + (i++ * 4)); | 33 | *val++ = readl(priv->base + reg + (i++ * 4)); |
| 30 | 34 | ||
| 31 | return 0; | 35 | return 0; |
| 32 | } | 36 | } |
| @@ -34,12 +38,12 @@ static int mtk_reg_read(void *context, | |||
| 34 | static int mtk_reg_write(void *context, | 38 | static int mtk_reg_write(void *context, |
| 35 | unsigned int reg, void *_val, size_t bytes) | 39 | unsigned int reg, void *_val, size_t bytes) |
| 36 | { | 40 | { |
| 37 | void __iomem *base = context; | 41 | struct mtk_efuse_priv *priv = context; |
| 38 | u32 *val = _val; | 42 | u32 *val = _val; |
| 39 | int i = 0, words = bytes / 4; | 43 | int i = 0, words = bytes / 4; |
| 40 | 44 | ||
| 41 | while (words--) | 45 | while (words--) |
| 42 | writel(*val++, base + reg + (i++ * 4)); | 46 | writel(*val++, priv->base + reg + (i++ * 4)); |
| 43 | 47 | ||
| 44 | return 0; | 48 | return 0; |
| 45 | } | 49 | } |
| @@ -49,27 +53,26 @@ static int mtk_efuse_probe(struct platform_device *pdev) | |||
| 49 | struct device *dev = &pdev->dev; | 53 | struct device *dev = &pdev->dev; |
| 50 | struct resource *res; | 54 | struct resource *res; |
| 51 | struct nvmem_device *nvmem; | 55 | struct nvmem_device *nvmem; |
| 52 | struct nvmem_config *econfig; | 56 | struct nvmem_config econfig = {}; |
| 53 | void __iomem *base; | 57 | struct mtk_efuse_priv *priv; |
| 54 | 58 | ||
| 55 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 59 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
| 56 | base = devm_ioremap_resource(dev, res); | 60 | if (!priv) |
| 57 | if (IS_ERR(base)) | ||
| 58 | return PTR_ERR(base); | ||
| 59 | |||
| 60 | econfig = devm_kzalloc(dev, sizeof(*econfig), GFP_KERNEL); | ||
| 61 | if (!econfig) | ||
| 62 | return -ENOMEM; | 61 | return -ENOMEM; |
| 63 | 62 | ||
| 64 | econfig->stride = 4; | 63 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 65 | econfig->word_size = 4; | 64 | priv->base = devm_ioremap_resource(dev, res); |
| 66 | econfig->reg_read = mtk_reg_read; | 65 | if (IS_ERR(priv->base)) |
| 67 | econfig->reg_write = mtk_reg_write; | 66 | return PTR_ERR(priv->base); |
| 68 | econfig->size = resource_size(res); | 67 | |
| 69 | econfig->priv = base; | 68 | econfig.stride = 4; |
| 70 | econfig->dev = dev; | 69 | econfig.word_size = 4; |
| 71 | econfig->owner = THIS_MODULE; | 70 | econfig.reg_read = mtk_reg_read; |
| 72 | nvmem = nvmem_register(econfig); | 71 | econfig.reg_write = mtk_reg_write; |
| 72 | econfig.size = resource_size(res); | ||
| 73 | econfig.priv = priv; | ||
| 74 | econfig.dev = dev; | ||
| 75 | nvmem = nvmem_register(&econfig); | ||
| 73 | if (IS_ERR(nvmem)) | 76 | if (IS_ERR(nvmem)) |
| 74 | return PTR_ERR(nvmem); | 77 | return PTR_ERR(nvmem); |
| 75 | 78 | ||
diff --git a/drivers/nvmem/mxs-ocotp.c b/drivers/nvmem/mxs-ocotp.c index d26dd03cec80..7018e2ef5714 100644 --- a/drivers/nvmem/mxs-ocotp.c +++ b/drivers/nvmem/mxs-ocotp.c | |||
| @@ -118,7 +118,6 @@ static struct nvmem_config ocotp_config = { | |||
| 118 | .name = "mxs-ocotp", | 118 | .name = "mxs-ocotp", |
| 119 | .stride = 16, | 119 | .stride = 16, |
| 120 | .word_size = 4, | 120 | .word_size = 4, |
| 121 | .owner = THIS_MODULE, | ||
| 122 | .reg_read = mxs_ocotp_read, | 121 | .reg_read = mxs_ocotp_read, |
| 123 | }; | 122 | }; |
| 124 | 123 | ||
diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c index 2bdb6c389328..cb3b48b47d64 100644 --- a/drivers/nvmem/qfprom.c +++ b/drivers/nvmem/qfprom.c | |||
| @@ -17,15 +17,19 @@ | |||
| 17 | #include <linux/nvmem-provider.h> | 17 | #include <linux/nvmem-provider.h> |
| 18 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
| 19 | 19 | ||
| 20 | struct qfprom_priv { | ||
| 21 | void __iomem *base; | ||
| 22 | }; | ||
| 23 | |||
| 20 | static int qfprom_reg_read(void *context, | 24 | static int qfprom_reg_read(void *context, |
| 21 | unsigned int reg, void *_val, size_t bytes) | 25 | unsigned int reg, void *_val, size_t bytes) |
| 22 | { | 26 | { |
| 23 | void __iomem *base = context; | 27 | struct qfprom_priv *priv = context; |
| 24 | u8 *val = _val; | 28 | u8 *val = _val; |
| 25 | int i = 0, words = bytes; | 29 | int i = 0, words = bytes; |
| 26 | 30 | ||
| 27 | while (words--) | 31 | while (words--) |
| 28 | *val++ = readb(base + reg + i++); | 32 | *val++ = readb(priv->base + reg + i++); |
| 29 | 33 | ||
| 30 | return 0; | 34 | return 0; |
| 31 | } | 35 | } |
| @@ -33,12 +37,12 @@ static int qfprom_reg_read(void *context, | |||
| 33 | static int qfprom_reg_write(void *context, | 37 | static int qfprom_reg_write(void *context, |
| 34 | unsigned int reg, void *_val, size_t bytes) | 38 | unsigned int reg, void *_val, size_t bytes) |
| 35 | { | 39 | { |
| 36 | void __iomem *base = context; | 40 | struct qfprom_priv *priv = context; |
| 37 | u8 *val = _val; | 41 | u8 *val = _val; |
| 38 | int i = 0, words = bytes; | 42 | int i = 0, words = bytes; |
| 39 | 43 | ||
| 40 | while (words--) | 44 | while (words--) |
| 41 | writeb(*val++, base + reg + i++); | 45 | writeb(*val++, priv->base + reg + i++); |
| 42 | 46 | ||
| 43 | return 0; | 47 | return 0; |
| 44 | } | 48 | } |
| @@ -52,7 +56,6 @@ static int qfprom_remove(struct platform_device *pdev) | |||
| 52 | 56 | ||
| 53 | static struct nvmem_config econfig = { | 57 | static struct nvmem_config econfig = { |
| 54 | .name = "qfprom", | 58 | .name = "qfprom", |
| 55 | .owner = THIS_MODULE, | ||
| 56 | .stride = 1, | 59 | .stride = 1, |
| 57 | .word_size = 1, | 60 | .word_size = 1, |
| 58 | .reg_read = qfprom_reg_read, | 61 | .reg_read = qfprom_reg_read, |
| @@ -64,16 +67,20 @@ static int qfprom_probe(struct platform_device *pdev) | |||
| 64 | struct device *dev = &pdev->dev; | 67 | struct device *dev = &pdev->dev; |
| 65 | struct resource *res; | 68 | struct resource *res; |
| 66 | struct nvmem_device *nvmem; | 69 | struct nvmem_device *nvmem; |
| 67 | void __iomem *base; | 70 | struct qfprom_priv *priv; |
| 71 | |||
| 72 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
| 73 | if (!priv) | ||
| 74 | return -ENOMEM; | ||
| 68 | 75 | ||
| 69 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 76 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 70 | base = devm_ioremap_resource(dev, res); | 77 | priv->base = devm_ioremap_resource(dev, res); |
| 71 | if (IS_ERR(base)) | 78 | if (IS_ERR(priv->base)) |
| 72 | return PTR_ERR(base); | 79 | return PTR_ERR(priv->base); |
| 73 | 80 | ||
| 74 | econfig.size = resource_size(res); | 81 | econfig.size = resource_size(res); |
| 75 | econfig.dev = dev; | 82 | econfig.dev = dev; |
| 76 | econfig.priv = base; | 83 | econfig.priv = priv; |
| 77 | 84 | ||
| 78 | nvmem = nvmem_register(&econfig); | 85 | nvmem = nvmem_register(&econfig); |
| 79 | if (IS_ERR(nvmem)) | 86 | if (IS_ERR(nvmem)) |
diff --git a/drivers/nvmem/rockchip-efuse.c b/drivers/nvmem/rockchip-efuse.c index 63e3eb55f3ac..123de77ca5d6 100644 --- a/drivers/nvmem/rockchip-efuse.c +++ b/drivers/nvmem/rockchip-efuse.c | |||
| @@ -149,7 +149,6 @@ static int rockchip_rk3399_efuse_read(void *context, unsigned int offset, | |||
| 149 | 149 | ||
| 150 | static struct nvmem_config econfig = { | 150 | static struct nvmem_config econfig = { |
| 151 | .name = "rockchip-efuse", | 151 | .name = "rockchip-efuse", |
| 152 | .owner = THIS_MODULE, | ||
| 153 | .stride = 1, | 152 | .stride = 1, |
| 154 | .word_size = 1, | 153 | .word_size = 1, |
| 155 | .read_only = true, | 154 | .read_only = true, |
| @@ -178,6 +177,10 @@ static const struct of_device_id rockchip_efuse_match[] = { | |||
| 178 | .data = (void *)&rockchip_rk3288_efuse_read, | 177 | .data = (void *)&rockchip_rk3288_efuse_read, |
| 179 | }, | 178 | }, |
| 180 | { | 179 | { |
| 180 | .compatible = "rockchip,rk3368-efuse", | ||
| 181 | .data = (void *)&rockchip_rk3288_efuse_read, | ||
| 182 | }, | ||
| 183 | { | ||
| 181 | .compatible = "rockchip,rk3399-efuse", | 184 | .compatible = "rockchip,rk3399-efuse", |
| 182 | .data = (void *)&rockchip_rk3399_efuse_read, | 185 | .data = (void *)&rockchip_rk3399_efuse_read, |
| 183 | }, | 186 | }, |
diff --git a/drivers/nvmem/snvs_lpgpr.c b/drivers/nvmem/snvs_lpgpr.c new file mode 100644 index 000000000000..e5c2a4a17f03 --- /dev/null +++ b/drivers/nvmem/snvs_lpgpr.c | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de> | ||
| 3 | * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License version 2 | ||
| 7 | * as published by the Free Software Foundation. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/mfd/syscon.h> | ||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/nvmem-provider.h> | ||
| 13 | #include <linux/of_device.h> | ||
| 14 | #include <linux/regmap.h> | ||
| 15 | |||
| 16 | #define IMX6Q_SNVS_HPLR 0x00 | ||
| 17 | #define IMX6Q_GPR_SL BIT(5) | ||
| 18 | #define IMX6Q_SNVS_LPLR 0x34 | ||
| 19 | #define IMX6Q_GPR_HL BIT(5) | ||
| 20 | #define IMX6Q_SNVS_LPGPR 0x68 | ||
| 21 | |||
| 22 | struct snvs_lpgpr_cfg { | ||
| 23 | int offset; | ||
| 24 | int offset_hplr; | ||
| 25 | int offset_lplr; | ||
| 26 | }; | ||
| 27 | |||
| 28 | struct snvs_lpgpr_priv { | ||
| 29 | struct device_d *dev; | ||
| 30 | struct regmap *regmap; | ||
| 31 | struct nvmem_config cfg; | ||
| 32 | const struct snvs_lpgpr_cfg *dcfg; | ||
| 33 | }; | ||
| 34 | |||
| 35 | static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q = { | ||
| 36 | .offset = IMX6Q_SNVS_LPGPR, | ||
| 37 | .offset_hplr = IMX6Q_SNVS_HPLR, | ||
| 38 | .offset_lplr = IMX6Q_SNVS_LPLR, | ||
| 39 | }; | ||
| 40 | |||
| 41 | static int snvs_lpgpr_write(void *context, unsigned int offset, void *val, | ||
| 42 | size_t bytes) | ||
| 43 | { | ||
| 44 | struct snvs_lpgpr_priv *priv = context; | ||
| 45 | const struct snvs_lpgpr_cfg *dcfg = priv->dcfg; | ||
| 46 | unsigned int lock_reg; | ||
| 47 | int ret; | ||
| 48 | |||
| 49 | ret = regmap_read(priv->regmap, dcfg->offset_hplr, &lock_reg); | ||
| 50 | if (ret < 0) | ||
| 51 | return ret; | ||
| 52 | |||
| 53 | if (lock_reg & IMX6Q_GPR_SL) | ||
| 54 | return -EPERM; | ||
| 55 | |||
| 56 | ret = regmap_read(priv->regmap, dcfg->offset_lplr, &lock_reg); | ||
| 57 | if (ret < 0) | ||
| 58 | return ret; | ||
| 59 | |||
| 60 | if (lock_reg & IMX6Q_GPR_HL) | ||
| 61 | return -EPERM; | ||
| 62 | |||
| 63 | return regmap_bulk_write(priv->regmap, dcfg->offset + offset, val, | ||
| 64 | bytes / 4); | ||
| 65 | } | ||
| 66 | |||
| 67 | static int snvs_lpgpr_read(void *context, unsigned int offset, void *val, | ||
| 68 | size_t bytes) | ||
| 69 | { | ||
| 70 | struct snvs_lpgpr_priv *priv = context; | ||
| 71 | const struct snvs_lpgpr_cfg *dcfg = priv->dcfg; | ||
| 72 | |||
| 73 | return regmap_bulk_read(priv->regmap, dcfg->offset + offset, | ||
| 74 | val, bytes / 4); | ||
| 75 | } | ||
| 76 | |||
| 77 | static int snvs_lpgpr_probe(struct platform_device *pdev) | ||
| 78 | { | ||
| 79 | struct device *dev = &pdev->dev; | ||
| 80 | struct device_node *node = dev->of_node; | ||
| 81 | struct device_node *syscon_node; | ||
| 82 | struct snvs_lpgpr_priv *priv; | ||
| 83 | struct nvmem_config *cfg; | ||
| 84 | struct nvmem_device *nvmem; | ||
| 85 | const struct snvs_lpgpr_cfg *dcfg; | ||
| 86 | |||
| 87 | if (!node) | ||
| 88 | return -ENOENT; | ||
| 89 | |||
| 90 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
| 91 | if (!priv) | ||
| 92 | return -ENOMEM; | ||
| 93 | |||
| 94 | dcfg = of_device_get_match_data(dev); | ||
| 95 | if (!dcfg) | ||
| 96 | return -EINVAL; | ||
| 97 | |||
| 98 | syscon_node = of_get_parent(node); | ||
| 99 | if (!syscon_node) | ||
| 100 | return -ENODEV; | ||
| 101 | |||
| 102 | priv->regmap = syscon_node_to_regmap(syscon_node); | ||
| 103 | of_node_put(syscon_node); | ||
| 104 | if (IS_ERR(priv->regmap)) | ||
| 105 | return PTR_ERR(priv->regmap); | ||
| 106 | |||
| 107 | priv->dcfg = dcfg; | ||
| 108 | |||
| 109 | cfg = &priv->cfg; | ||
| 110 | cfg->priv = priv; | ||
| 111 | cfg->name = dev_name(dev); | ||
| 112 | cfg->dev = dev; | ||
| 113 | cfg->stride = 4, | ||
| 114 | cfg->word_size = 4, | ||
| 115 | cfg->size = 4, | ||
| 116 | cfg->owner = THIS_MODULE, | ||
| 117 | cfg->reg_read = snvs_lpgpr_read, | ||
| 118 | cfg->reg_write = snvs_lpgpr_write, | ||
| 119 | |||
| 120 | nvmem = nvmem_register(cfg); | ||
| 121 | if (IS_ERR(nvmem)) | ||
| 122 | return PTR_ERR(nvmem); | ||
| 123 | |||
| 124 | platform_set_drvdata(pdev, nvmem); | ||
| 125 | |||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | static int snvs_lpgpr_remove(struct platform_device *pdev) | ||
| 130 | { | ||
| 131 | struct nvmem_device *nvmem = platform_get_drvdata(pdev); | ||
| 132 | |||
| 133 | return nvmem_unregister(nvmem); | ||
| 134 | } | ||
| 135 | |||
| 136 | static const struct of_device_id snvs_lpgpr_dt_ids[] = { | ||
| 137 | { .compatible = "fsl,imx6q-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx6q }, | ||
| 138 | { .compatible = "fsl,imx6ul-snvs-lpgpr", | ||
| 139 | .data = &snvs_lpgpr_cfg_imx6q }, | ||
| 140 | { }, | ||
| 141 | }; | ||
| 142 | MODULE_DEVICE_TABLE(of, snvs_lpgpr_dt_ids); | ||
| 143 | |||
| 144 | static struct platform_driver snvs_lpgpr_driver = { | ||
| 145 | .probe = snvs_lpgpr_probe, | ||
| 146 | .remove = snvs_lpgpr_remove, | ||
| 147 | .driver = { | ||
| 148 | .name = "snvs_lpgpr", | ||
| 149 | .of_match_table = snvs_lpgpr_dt_ids, | ||
| 150 | }, | ||
| 151 | }; | ||
| 152 | module_platform_driver(snvs_lpgpr_driver); | ||
| 153 | |||
| 154 | MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); | ||
| 155 | MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 Secure Non-Volatile Storage"); | ||
| 156 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c index 0d6648be93b8..99bd54d85fcb 100644 --- a/drivers/nvmem/sunxi_sid.c +++ b/drivers/nvmem/sunxi_sid.c | |||
| @@ -40,7 +40,6 @@ static struct nvmem_config econfig = { | |||
| 40 | .read_only = true, | 40 | .read_only = true, |
| 41 | .stride = 4, | 41 | .stride = 4, |
| 42 | .word_size = 1, | 42 | .word_size = 1, |
| 43 | .owner = THIS_MODULE, | ||
| 44 | }; | 43 | }; |
| 45 | 44 | ||
| 46 | struct sunxi_sid_cfg { | 45 | struct sunxi_sid_cfg { |
| @@ -199,10 +198,16 @@ static const struct sunxi_sid_cfg sun8i_h3_cfg = { | |||
| 199 | .need_register_readout = true, | 198 | .need_register_readout = true, |
| 200 | }; | 199 | }; |
| 201 | 200 | ||
| 201 | static const struct sunxi_sid_cfg sun50i_a64_cfg = { | ||
| 202 | .value_offset = 0x200, | ||
| 203 | .size = 0x100, | ||
| 204 | }; | ||
| 205 | |||
| 202 | static const struct of_device_id sunxi_sid_of_match[] = { | 206 | static const struct of_device_id sunxi_sid_of_match[] = { |
| 203 | { .compatible = "allwinner,sun4i-a10-sid", .data = &sun4i_a10_cfg }, | 207 | { .compatible = "allwinner,sun4i-a10-sid", .data = &sun4i_a10_cfg }, |
| 204 | { .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg }, | 208 | { .compatible = "allwinner,sun7i-a20-sid", .data = &sun7i_a20_cfg }, |
| 205 | { .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg }, | 209 | { .compatible = "allwinner,sun8i-h3-sid", .data = &sun8i_h3_cfg }, |
| 210 | { .compatible = "allwinner,sun50i-a64-sid", .data = &sun50i_a64_cfg }, | ||
| 206 | {/* sentinel */}, | 211 | {/* sentinel */}, |
| 207 | }; | 212 | }; |
| 208 | MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); | 213 | MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); |
diff --git a/drivers/nvmem/uniphier-efuse.c b/drivers/nvmem/uniphier-efuse.c new file mode 100644 index 000000000000..9d278b4e1dc7 --- /dev/null +++ b/drivers/nvmem/uniphier-efuse.c | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | /* | ||
| 2 | * UniPhier eFuse driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2017 Socionext Inc. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/device.h> | ||
| 17 | #include <linux/io.h> | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/nvmem-provider.h> | ||
| 20 | #include <linux/platform_device.h> | ||
| 21 | |||
| 22 | struct uniphier_efuse_priv { | ||
| 23 | void __iomem *base; | ||
| 24 | }; | ||
| 25 | |||
| 26 | static int uniphier_reg_read(void *context, | ||
| 27 | unsigned int reg, void *_val, size_t bytes) | ||
| 28 | { | ||
| 29 | struct uniphier_efuse_priv *priv = context; | ||
| 30 | u32 *val = _val; | ||
| 31 | int offs; | ||
| 32 | |||
| 33 | for (offs = 0; offs < bytes; offs += sizeof(u32)) | ||
| 34 | *val++ = readl(priv->base + reg + offs); | ||
| 35 | |||
| 36 | return 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | static int uniphier_efuse_probe(struct platform_device *pdev) | ||
| 40 | { | ||
| 41 | struct device *dev = &pdev->dev; | ||
| 42 | struct resource *res; | ||
| 43 | struct nvmem_device *nvmem; | ||
| 44 | struct nvmem_config econfig = {}; | ||
| 45 | struct uniphier_efuse_priv *priv; | ||
| 46 | |||
| 47 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
| 48 | if (!priv) | ||
| 49 | return -ENOMEM; | ||
| 50 | |||
| 51 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 52 | priv->base = devm_ioremap_resource(dev, res); | ||
| 53 | if (IS_ERR(priv->base)) | ||
| 54 | return PTR_ERR(priv->base); | ||
| 55 | |||
| 56 | econfig.stride = 4; | ||
| 57 | econfig.word_size = 4; | ||
| 58 | econfig.read_only = true; | ||
| 59 | econfig.reg_read = uniphier_reg_read; | ||
| 60 | econfig.size = resource_size(res); | ||
| 61 | econfig.priv = priv; | ||
| 62 | econfig.dev = dev; | ||
| 63 | nvmem = nvmem_register(&econfig); | ||
| 64 | if (IS_ERR(nvmem)) | ||
| 65 | return PTR_ERR(nvmem); | ||
| 66 | |||
| 67 | platform_set_drvdata(pdev, nvmem); | ||
| 68 | |||
| 69 | return 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | static int uniphier_efuse_remove(struct platform_device *pdev) | ||
| 73 | { | ||
| 74 | struct nvmem_device *nvmem = platform_get_drvdata(pdev); | ||
| 75 | |||
| 76 | return nvmem_unregister(nvmem); | ||
| 77 | } | ||
| 78 | |||
| 79 | static const struct of_device_id uniphier_efuse_of_match[] = { | ||
| 80 | { .compatible = "socionext,uniphier-efuse",}, | ||
| 81 | {/* sentinel */}, | ||
| 82 | }; | ||
| 83 | MODULE_DEVICE_TABLE(of, uniphier_efuse_of_match); | ||
| 84 | |||
| 85 | static struct platform_driver uniphier_efuse_driver = { | ||
| 86 | .probe = uniphier_efuse_probe, | ||
| 87 | .remove = uniphier_efuse_remove, | ||
| 88 | .driver = { | ||
| 89 | .name = "uniphier-efuse", | ||
| 90 | .of_match_table = uniphier_efuse_of_match, | ||
| 91 | }, | ||
| 92 | }; | ||
| 93 | module_platform_driver(uniphier_efuse_driver); | ||
| 94 | |||
| 95 | MODULE_AUTHOR("Keiji Hayashibara <hayashibara.keiji@socionext.com>"); | ||
| 96 | MODULE_DESCRIPTION("UniPhier eFuse driver"); | ||
| 97 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/nvmem/vf610-ocotp.c b/drivers/nvmem/vf610-ocotp.c index 72e4faabce29..5ae9e002f195 100644 --- a/drivers/nvmem/vf610-ocotp.c +++ b/drivers/nvmem/vf610-ocotp.c | |||
| @@ -206,7 +206,6 @@ static int vf610_ocotp_read(void *context, unsigned int offset, | |||
| 206 | 206 | ||
| 207 | static struct nvmem_config ocotp_config = { | 207 | static struct nvmem_config ocotp_config = { |
| 208 | .name = "ocotp", | 208 | .name = "ocotp", |
| 209 | .owner = THIS_MODULE, | ||
| 210 | .stride = 4, | 209 | .stride = 4, |
| 211 | .word_size = 4, | 210 | .word_size = 4, |
| 212 | .reg_read = vf610_ocotp_read, | 211 | .reg_read = vf610_ocotp_read, |
