cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

lpc18xx_eeprom.c (7137B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * NXP LPC18xx/LPC43xx EEPROM memory NVMEM driver
      4 *
      5 * Copyright (c) 2015 Ariel D'Alessandro <ariel@vanguardiasur.com>
      6 */
      7
      8#include <linux/clk.h>
      9#include <linux/device.h>
     10#include <linux/delay.h>
     11#include <linux/err.h>
     12#include <linux/io.h>
     13#include <linux/module.h>
     14#include <linux/mod_devicetable.h>
     15#include <linux/nvmem-provider.h>
     16#include <linux/platform_device.h>
     17#include <linux/reset.h>
     18
     19/* Registers */
     20#define LPC18XX_EEPROM_AUTOPROG			0x00c
     21#define LPC18XX_EEPROM_AUTOPROG_WORD		0x1
     22
     23#define LPC18XX_EEPROM_CLKDIV			0x014
     24
     25#define LPC18XX_EEPROM_PWRDWN			0x018
     26#define LPC18XX_EEPROM_PWRDWN_NO		0x0
     27#define LPC18XX_EEPROM_PWRDWN_YES		0x1
     28
     29#define LPC18XX_EEPROM_INTSTAT			0xfe0
     30#define LPC18XX_EEPROM_INTSTAT_END_OF_PROG	BIT(2)
     31
     32#define LPC18XX_EEPROM_INTSTATCLR		0xfe8
     33#define LPC18XX_EEPROM_INTSTATCLR_PROG_CLR_ST	BIT(2)
     34
     35/* Fixed page size (bytes) */
     36#define LPC18XX_EEPROM_PAGE_SIZE		0x80
     37
     38/* EEPROM device requires a ~1500 kHz clock (min 800 kHz, max 1600 kHz) */
     39#define LPC18XX_EEPROM_CLOCK_HZ			1500000
     40
     41/* EEPROM requires 3 ms of erase/program time between each writing */
     42#define LPC18XX_EEPROM_PROGRAM_TIME		3
     43
     44struct lpc18xx_eeprom_dev {
     45	struct clk *clk;
     46	void __iomem *reg_base;
     47	void __iomem *mem_base;
     48	struct nvmem_device *nvmem;
     49	unsigned reg_bytes;
     50	unsigned val_bytes;
     51	int size;
     52};
     53
     54static inline void lpc18xx_eeprom_writel(struct lpc18xx_eeprom_dev *eeprom,
     55					 u32 reg, u32 val)
     56{
     57	writel(val, eeprom->reg_base + reg);
     58}
     59
     60static inline u32 lpc18xx_eeprom_readl(struct lpc18xx_eeprom_dev *eeprom,
     61				       u32 reg)
     62{
     63	return readl(eeprom->reg_base + reg);
     64}
     65
     66static int lpc18xx_eeprom_busywait_until_prog(struct lpc18xx_eeprom_dev *eeprom)
     67{
     68	unsigned long end;
     69	u32 val;
     70
     71	/* Wait until EEPROM program operation has finished */
     72	end = jiffies + msecs_to_jiffies(LPC18XX_EEPROM_PROGRAM_TIME * 10);
     73
     74	while (time_is_after_jiffies(end)) {
     75		val = lpc18xx_eeprom_readl(eeprom, LPC18XX_EEPROM_INTSTAT);
     76
     77		if (val & LPC18XX_EEPROM_INTSTAT_END_OF_PROG) {
     78			lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_INTSTATCLR,
     79					LPC18XX_EEPROM_INTSTATCLR_PROG_CLR_ST);
     80			return 0;
     81		}
     82
     83		usleep_range(LPC18XX_EEPROM_PROGRAM_TIME * USEC_PER_MSEC,
     84			     (LPC18XX_EEPROM_PROGRAM_TIME + 1) * USEC_PER_MSEC);
     85	}
     86
     87	return -ETIMEDOUT;
     88}
     89
     90static int lpc18xx_eeprom_gather_write(void *context, unsigned int reg,
     91				       void *val, size_t bytes)
     92{
     93	struct lpc18xx_eeprom_dev *eeprom = context;
     94	unsigned int offset = reg;
     95	int ret;
     96
     97	/*
     98	 * The last page contains the EEPROM initialization data and is not
     99	 * writable.
    100	 */
    101	if ((reg > eeprom->size - LPC18XX_EEPROM_PAGE_SIZE) ||
    102			(reg + bytes > eeprom->size - LPC18XX_EEPROM_PAGE_SIZE))
    103		return -EINVAL;
    104
    105
    106	lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
    107			      LPC18XX_EEPROM_PWRDWN_NO);
    108
    109	/* Wait 100 us while the EEPROM wakes up */
    110	usleep_range(100, 200);
    111
    112	while (bytes) {
    113		writel(*(u32 *)val, eeprom->mem_base + offset);
    114		ret = lpc18xx_eeprom_busywait_until_prog(eeprom);
    115		if (ret < 0)
    116			return ret;
    117
    118		bytes -= eeprom->val_bytes;
    119		val += eeprom->val_bytes;
    120		offset += eeprom->val_bytes;
    121	}
    122
    123	lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
    124			      LPC18XX_EEPROM_PWRDWN_YES);
    125
    126	return 0;
    127}
    128
    129static int lpc18xx_eeprom_read(void *context, unsigned int offset,
    130			       void *val, size_t bytes)
    131{
    132	struct lpc18xx_eeprom_dev *eeprom = context;
    133
    134	lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
    135			      LPC18XX_EEPROM_PWRDWN_NO);
    136
    137	/* Wait 100 us while the EEPROM wakes up */
    138	usleep_range(100, 200);
    139
    140	while (bytes) {
    141		*(u32 *)val = readl(eeprom->mem_base + offset);
    142		bytes -= eeprom->val_bytes;
    143		val += eeprom->val_bytes;
    144		offset += eeprom->val_bytes;
    145	}
    146
    147	lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
    148			      LPC18XX_EEPROM_PWRDWN_YES);
    149
    150	return 0;
    151}
    152
    153
    154static struct nvmem_config lpc18xx_nvmem_config = {
    155	.name = "lpc18xx-eeprom",
    156	.stride = 4,
    157	.word_size = 4,
    158	.reg_read = lpc18xx_eeprom_read,
    159	.reg_write = lpc18xx_eeprom_gather_write,
    160};
    161
    162static int lpc18xx_eeprom_probe(struct platform_device *pdev)
    163{
    164	struct lpc18xx_eeprom_dev *eeprom;
    165	struct device *dev = &pdev->dev;
    166	struct reset_control *rst;
    167	unsigned long clk_rate;
    168	struct resource *res;
    169	int ret;
    170
    171	eeprom = devm_kzalloc(dev, sizeof(*eeprom), GFP_KERNEL);
    172	if (!eeprom)
    173		return -ENOMEM;
    174
    175	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg");
    176	eeprom->reg_base = devm_ioremap_resource(dev, res);
    177	if (IS_ERR(eeprom->reg_base))
    178		return PTR_ERR(eeprom->reg_base);
    179
    180	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem");
    181	eeprom->mem_base = devm_ioremap_resource(dev, res);
    182	if (IS_ERR(eeprom->mem_base))
    183		return PTR_ERR(eeprom->mem_base);
    184
    185	eeprom->clk = devm_clk_get(&pdev->dev, "eeprom");
    186	if (IS_ERR(eeprom->clk)) {
    187		dev_err(&pdev->dev, "failed to get eeprom clock\n");
    188		return PTR_ERR(eeprom->clk);
    189	}
    190
    191	ret = clk_prepare_enable(eeprom->clk);
    192	if (ret < 0) {
    193		dev_err(dev, "failed to prepare/enable eeprom clk: %d\n", ret);
    194		return ret;
    195	}
    196
    197	rst = devm_reset_control_get_exclusive(dev, NULL);
    198	if (IS_ERR(rst)) {
    199		dev_err(dev, "failed to get reset: %ld\n", PTR_ERR(rst));
    200		ret = PTR_ERR(rst);
    201		goto err_clk;
    202	}
    203
    204	ret = reset_control_assert(rst);
    205	if (ret < 0) {
    206		dev_err(dev, "failed to assert reset: %d\n", ret);
    207		goto err_clk;
    208	}
    209
    210	eeprom->val_bytes = 4;
    211	eeprom->reg_bytes = 4;
    212
    213	/*
    214	 * Clock rate is generated by dividing the system bus clock by the
    215	 * division factor, contained in the divider register (minus 1 encoded).
    216	 */
    217	clk_rate = clk_get_rate(eeprom->clk);
    218	clk_rate = DIV_ROUND_UP(clk_rate, LPC18XX_EEPROM_CLOCK_HZ) - 1;
    219	lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_CLKDIV, clk_rate);
    220
    221	/*
    222	 * Writing a single word to the page will start the erase/program cycle
    223	 * automatically
    224	 */
    225	lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_AUTOPROG,
    226			      LPC18XX_EEPROM_AUTOPROG_WORD);
    227
    228	lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
    229			      LPC18XX_EEPROM_PWRDWN_YES);
    230
    231	eeprom->size = resource_size(res);
    232	lpc18xx_nvmem_config.size = resource_size(res);
    233	lpc18xx_nvmem_config.dev = dev;
    234	lpc18xx_nvmem_config.priv = eeprom;
    235
    236	eeprom->nvmem = devm_nvmem_register(dev, &lpc18xx_nvmem_config);
    237	if (IS_ERR(eeprom->nvmem)) {
    238		ret = PTR_ERR(eeprom->nvmem);
    239		goto err_clk;
    240	}
    241
    242	platform_set_drvdata(pdev, eeprom);
    243
    244	return 0;
    245
    246err_clk:
    247	clk_disable_unprepare(eeprom->clk);
    248
    249	return ret;
    250}
    251
    252static int lpc18xx_eeprom_remove(struct platform_device *pdev)
    253{
    254	struct lpc18xx_eeprom_dev *eeprom = platform_get_drvdata(pdev);
    255
    256	clk_disable_unprepare(eeprom->clk);
    257
    258	return 0;
    259}
    260
    261static const struct of_device_id lpc18xx_eeprom_of_match[] = {
    262	{ .compatible = "nxp,lpc1857-eeprom" },
    263	{ },
    264};
    265MODULE_DEVICE_TABLE(of, lpc18xx_eeprom_of_match);
    266
    267static struct platform_driver lpc18xx_eeprom_driver = {
    268	.probe = lpc18xx_eeprom_probe,
    269	.remove = lpc18xx_eeprom_remove,
    270	.driver = {
    271		.name = "lpc18xx-eeprom",
    272		.of_match_table = lpc18xx_eeprom_of_match,
    273	},
    274};
    275
    276module_platform_driver(lpc18xx_eeprom_driver);
    277
    278MODULE_AUTHOR("Ariel D'Alessandro <ariel@vanguardiasur.com.ar>");
    279MODULE_DESCRIPTION("NXP LPC18xx EEPROM memory Driver");
    280MODULE_LICENSE("GPL v2");