brintos

brintos / linux-shallow public Read only

0
0
Text · 6.5 KiB · 8a16f5f Raw
244 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Amlogic Meson6, Meson8 and Meson8b eFuse Driver4 *5 * Copyright (c) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>6 */7 8#include <linux/bitfield.h>9#include <linux/bitops.h>10#include <linux/clk.h>11#include <linux/delay.h>12#include <linux/io.h>13#include <linux/iopoll.h>14#include <linux/module.h>15#include <linux/nvmem-provider.h>16#include <linux/of.h>17#include <linux/platform_device.h>18#include <linux/sizes.h>19#include <linux/slab.h>20 21#define MESON_MX_EFUSE_CNTL1					0x0422#define MESON_MX_EFUSE_CNTL1_PD_ENABLE				BIT(27)23#define MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY			BIT(26)24#define MESON_MX_EFUSE_CNTL1_AUTO_RD_START			BIT(25)25#define MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE			BIT(24)26#define MESON_MX_EFUSE_CNTL1_BYTE_WR_DATA			GENMASK(23, 16)27#define MESON_MX_EFUSE_CNTL1_AUTO_WR_BUSY			BIT(14)28#define MESON_MX_EFUSE_CNTL1_AUTO_WR_START			BIT(13)29#define MESON_MX_EFUSE_CNTL1_AUTO_WR_ENABLE			BIT(12)30#define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET			BIT(11)31#define MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK			GENMASK(10, 0)32 33#define MESON_MX_EFUSE_CNTL2					0x0834 35#define MESON_MX_EFUSE_CNTL4					0x1036#define MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE			BIT(10)37 38struct meson_mx_efuse_platform_data {39	const char *name;40	unsigned int word_size;41};42 43struct meson_mx_efuse {44	void __iomem *base;45	struct clk *core_clk;46	struct nvmem_config config;47};48 49static void meson_mx_efuse_mask_bits(struct meson_mx_efuse *efuse, u32 reg,50				     u32 mask, u32 set)51{52	u32 data;53 54	data = readl(efuse->base + reg);55	data &= ~mask;56	data |= (set & mask);57 58	writel(data, efuse->base + reg);59}60 61static int meson_mx_efuse_hw_enable(struct meson_mx_efuse *efuse)62{63	int err;64 65	err = clk_prepare_enable(efuse->core_clk);66	if (err)67		return err;68 69	/* power up the efuse */70	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,71				 MESON_MX_EFUSE_CNTL1_PD_ENABLE, 0);72 73	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL4,74				 MESON_MX_EFUSE_CNTL4_ENCRYPT_ENABLE, 0);75 76	return 0;77}78 79static void meson_mx_efuse_hw_disable(struct meson_mx_efuse *efuse)80{81	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,82				 MESON_MX_EFUSE_CNTL1_PD_ENABLE,83				 MESON_MX_EFUSE_CNTL1_PD_ENABLE);84 85	clk_disable_unprepare(efuse->core_clk);86}87 88static int meson_mx_efuse_read_addr(struct meson_mx_efuse *efuse,89				    unsigned int addr, u32 *value)90{91	int err;92	u32 regval;93 94	/* write the address to read */95	regval = FIELD_PREP(MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, addr);96	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,97				 MESON_MX_EFUSE_CNTL1_BYTE_ADDR_MASK, regval);98 99	/* inform the hardware that we changed the address */100	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,101				 MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET,102				 MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET);103	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,104				 MESON_MX_EFUSE_CNTL1_BYTE_ADDR_SET, 0);105 106	/* start the read process */107	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,108				 MESON_MX_EFUSE_CNTL1_AUTO_RD_START,109				 MESON_MX_EFUSE_CNTL1_AUTO_RD_START);110	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,111				 MESON_MX_EFUSE_CNTL1_AUTO_RD_START, 0);112 113	/*114	 * perform a dummy read to ensure that the HW has the RD_BUSY bit set115	 * when polling for the status below.116	 */117	readl(efuse->base + MESON_MX_EFUSE_CNTL1);118 119	err = readl_poll_timeout_atomic(efuse->base + MESON_MX_EFUSE_CNTL1,120			regval,121			(!(regval & MESON_MX_EFUSE_CNTL1_AUTO_RD_BUSY)),122			1, 1000);123	if (err) {124		dev_err(efuse->config.dev,125			"Timeout while reading efuse address %u\n", addr);126		return err;127	}128 129	*value = readl(efuse->base + MESON_MX_EFUSE_CNTL2);130 131	return 0;132}133 134static int meson_mx_efuse_read(void *context, unsigned int offset,135			       void *buf, size_t bytes)136{137	struct meson_mx_efuse *efuse = context;138	u32 tmp;139	int err, i, addr;140 141	err = meson_mx_efuse_hw_enable(efuse);142	if (err)143		return err;144 145	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,146				 MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE,147				 MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE);148 149	for (i = 0; i < bytes; i += efuse->config.word_size) {150		addr = (offset + i) / efuse->config.word_size;151 152		err = meson_mx_efuse_read_addr(efuse, addr, &tmp);153		if (err)154			break;155 156		memcpy(buf + i, &tmp,157		       min_t(size_t, bytes - i, efuse->config.word_size));158	}159 160	meson_mx_efuse_mask_bits(efuse, MESON_MX_EFUSE_CNTL1,161				 MESON_MX_EFUSE_CNTL1_AUTO_RD_ENABLE, 0);162 163	meson_mx_efuse_hw_disable(efuse);164 165	return err;166}167 168static const struct meson_mx_efuse_platform_data meson6_efuse_data = {169	.name = "meson6-efuse",170	.word_size = 1,171};172 173static const struct meson_mx_efuse_platform_data meson8_efuse_data = {174	.name = "meson8-efuse",175	.word_size = 4,176};177 178static const struct meson_mx_efuse_platform_data meson8b_efuse_data = {179	.name = "meson8b-efuse",180	.word_size = 4,181};182 183static const struct of_device_id meson_mx_efuse_match[] = {184	{ .compatible = "amlogic,meson6-efuse", .data = &meson6_efuse_data },185	{ .compatible = "amlogic,meson8-efuse", .data = &meson8_efuse_data },186	{ .compatible = "amlogic,meson8b-efuse", .data = &meson8b_efuse_data },187	{ /* sentinel */ },188};189MODULE_DEVICE_TABLE(of, meson_mx_efuse_match);190 191static int meson_mx_efuse_probe(struct platform_device *pdev)192{193	const struct meson_mx_efuse_platform_data *drvdata;194	struct meson_mx_efuse *efuse;195	struct nvmem_device *nvmem;196 197	drvdata = of_device_get_match_data(&pdev->dev);198	if (!drvdata)199		return -EINVAL;200 201	efuse = devm_kzalloc(&pdev->dev, sizeof(*efuse), GFP_KERNEL);202	if (!efuse)203		return -ENOMEM;204 205	efuse->base = devm_platform_ioremap_resource(pdev, 0);206	if (IS_ERR(efuse->base))207		return PTR_ERR(efuse->base);208 209	efuse->config.name = drvdata->name;210	efuse->config.owner = THIS_MODULE;211	efuse->config.dev = &pdev->dev;212	efuse->config.priv = efuse;213	efuse->config.add_legacy_fixed_of_cells = true;214	efuse->config.stride = drvdata->word_size;215	efuse->config.word_size = drvdata->word_size;216	efuse->config.size = SZ_512;217	efuse->config.read_only = true;218	efuse->config.reg_read = meson_mx_efuse_read;219 220	efuse->core_clk = devm_clk_get(&pdev->dev, "core");221	if (IS_ERR(efuse->core_clk)) {222		dev_err(&pdev->dev, "Failed to get core clock\n");223		return PTR_ERR(efuse->core_clk);224	}225 226	nvmem = devm_nvmem_register(&pdev->dev, &efuse->config);227 228	return PTR_ERR_OR_ZERO(nvmem);229}230 231static struct platform_driver meson_mx_efuse_driver = {232	.probe = meson_mx_efuse_probe,233	.driver = {234		.name = "meson-mx-efuse",235		.of_match_table = meson_mx_efuse_match,236	},237};238 239module_platform_driver(meson_mx_efuse_driver);240 241MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");242MODULE_DESCRIPTION("Amlogic Meson MX eFuse NVMEM driver");243MODULE_LICENSE("GPL v2");244