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

w1_ds250x.c (6418B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * w1_ds250x.c - w1 family 09/0b/89/91 (DS250x) driver
      4 */
      5
      6#include <linux/kernel.h>
      7#include <linux/module.h>
      8#include <linux/moduleparam.h>
      9#include <linux/device.h>
     10#include <linux/types.h>
     11#include <linux/delay.h>
     12#include <linux/slab.h>
     13#include <linux/crc16.h>
     14
     15#include <linux/w1.h>
     16#include <linux/nvmem-provider.h>
     17
     18#define W1_DS2501_UNW_FAMILY    0x91
     19#define W1_DS2501_SIZE          64
     20
     21#define W1_DS2502_FAMILY        0x09
     22#define W1_DS2502_UNW_FAMILY    0x89
     23#define W1_DS2502_SIZE          128
     24
     25#define W1_DS2505_FAMILY	0x0b
     26#define W1_DS2505_SIZE		2048
     27
     28#define W1_PAGE_SIZE		32
     29
     30#define W1_EXT_READ_MEMORY	0xA5
     31#define W1_READ_DATA_CRC        0xC3
     32
     33#define OFF2PG(off)	((off) / W1_PAGE_SIZE)
     34
     35#define CRC16_INIT		0
     36#define CRC16_VALID		0xb001
     37
     38struct w1_eprom_data {
     39	size_t size;
     40	int (*read)(struct w1_slave *sl, int pageno);
     41	u8 eprom[W1_DS2505_SIZE];
     42	DECLARE_BITMAP(page_present, W1_DS2505_SIZE / W1_PAGE_SIZE);
     43	char nvmem_name[64];
     44};
     45
     46static int w1_ds2502_read_page(struct w1_slave *sl, int pageno)
     47{
     48	struct w1_eprom_data *data = sl->family_data;
     49	int pgoff = pageno * W1_PAGE_SIZE;
     50	int ret = -EIO;
     51	u8 buf[3];
     52	u8 crc8;
     53
     54	if (test_bit(pageno, data->page_present))
     55		return 0; /* page already present */
     56
     57	mutex_lock(&sl->master->bus_mutex);
     58
     59	if (w1_reset_select_slave(sl))
     60		goto err;
     61
     62	buf[0] = W1_READ_DATA_CRC;
     63	buf[1] = pgoff & 0xff;
     64	buf[2] = pgoff >> 8;
     65	w1_write_block(sl->master, buf, 3);
     66
     67	crc8 = w1_read_8(sl->master);
     68	if (w1_calc_crc8(buf, 3) != crc8)
     69		goto err;
     70
     71	w1_read_block(sl->master, &data->eprom[pgoff], W1_PAGE_SIZE);
     72
     73	crc8 = w1_read_8(sl->master);
     74	if (w1_calc_crc8(&data->eprom[pgoff], W1_PAGE_SIZE) != crc8)
     75		goto err;
     76
     77	set_bit(pageno, data->page_present); /* mark page present */
     78	ret = 0;
     79err:
     80	mutex_unlock(&sl->master->bus_mutex);
     81	return ret;
     82}
     83
     84static int w1_ds2505_read_page(struct w1_slave *sl, int pageno)
     85{
     86	struct w1_eprom_data *data = sl->family_data;
     87	int redir_retries = 16;
     88	int pgoff, epoff;
     89	int ret = -EIO;
     90	u8 buf[6];
     91	u8 redir;
     92	u16 crc;
     93
     94	if (test_bit(pageno, data->page_present))
     95		return 0; /* page already present */
     96
     97	epoff = pgoff = pageno * W1_PAGE_SIZE;
     98	mutex_lock(&sl->master->bus_mutex);
     99
    100retry:
    101	if (w1_reset_select_slave(sl))
    102		goto err;
    103
    104	buf[0] = W1_EXT_READ_MEMORY;
    105	buf[1] = pgoff & 0xff;
    106	buf[2] = pgoff >> 8;
    107	w1_write_block(sl->master, buf, 3);
    108	w1_read_block(sl->master, buf + 3, 3); /* redir, crc16 */
    109	redir = buf[3];
    110	crc = crc16(CRC16_INIT, buf, 6);
    111
    112	if (crc != CRC16_VALID)
    113		goto err;
    114
    115
    116	if (redir != 0xff) {
    117		redir_retries--;
    118		if (redir_retries < 0)
    119			goto err;
    120
    121		pgoff = (redir ^ 0xff) * W1_PAGE_SIZE;
    122		goto retry;
    123	}
    124
    125	w1_read_block(sl->master, &data->eprom[epoff], W1_PAGE_SIZE);
    126	w1_read_block(sl->master, buf, 2); /* crc16 */
    127	crc = crc16(CRC16_INIT, &data->eprom[epoff], W1_PAGE_SIZE);
    128	crc = crc16(crc, buf, 2);
    129
    130	if (crc != CRC16_VALID)
    131		goto err;
    132
    133	set_bit(pageno, data->page_present);
    134	ret = 0;
    135err:
    136	mutex_unlock(&sl->master->bus_mutex);
    137	return ret;
    138}
    139
    140static int w1_nvmem_read(void *priv, unsigned int off, void *buf, size_t count)
    141{
    142	struct w1_slave *sl = priv;
    143	struct w1_eprom_data *data = sl->family_data;
    144	size_t eprom_size = data->size;
    145	int ret;
    146	int i;
    147
    148	if (off > eprom_size)
    149		return -EINVAL;
    150
    151	if ((off + count) > eprom_size)
    152		count = eprom_size - off;
    153
    154	i = OFF2PG(off);
    155	do {
    156		ret = data->read(sl, i++);
    157		if (ret < 0)
    158			return ret;
    159	} while (i < OFF2PG(off + count));
    160
    161	memcpy(buf, &data->eprom[off], count);
    162	return 0;
    163}
    164
    165static int w1_eprom_add_slave(struct w1_slave *sl)
    166{
    167	struct w1_eprom_data *data;
    168	struct nvmem_device *nvmem;
    169	struct nvmem_config nvmem_cfg = {
    170		.dev = &sl->dev,
    171		.reg_read = w1_nvmem_read,
    172		.type = NVMEM_TYPE_OTP,
    173		.read_only = true,
    174		.word_size = 1,
    175		.priv = sl,
    176		.id = -1
    177	};
    178
    179	data = devm_kzalloc(&sl->dev, sizeof(struct w1_eprom_data), GFP_KERNEL);
    180	if (!data)
    181		return -ENOMEM;
    182
    183	sl->family_data = data;
    184	switch (sl->family->fid) {
    185	case W1_DS2501_UNW_FAMILY:
    186		data->size = W1_DS2501_SIZE;
    187		data->read = w1_ds2502_read_page;
    188		break;
    189	case W1_DS2502_FAMILY:
    190	case W1_DS2502_UNW_FAMILY:
    191		data->size = W1_DS2502_SIZE;
    192		data->read = w1_ds2502_read_page;
    193		break;
    194	case W1_DS2505_FAMILY:
    195		data->size = W1_DS2505_SIZE;
    196		data->read = w1_ds2505_read_page;
    197		break;
    198	}
    199
    200	if (sl->master->bus_master->dev_id)
    201		snprintf(data->nvmem_name, sizeof(data->nvmem_name),
    202			 "%s-%02x-%012llx",
    203			 sl->master->bus_master->dev_id, sl->reg_num.family,
    204			 (unsigned long long)sl->reg_num.id);
    205	else
    206		snprintf(data->nvmem_name, sizeof(data->nvmem_name),
    207			 "%02x-%012llx",
    208			 sl->reg_num.family,
    209			 (unsigned long long)sl->reg_num.id);
    210
    211	nvmem_cfg.name = data->nvmem_name;
    212	nvmem_cfg.size = data->size;
    213
    214	nvmem = devm_nvmem_register(&sl->dev, &nvmem_cfg);
    215	return PTR_ERR_OR_ZERO(nvmem);
    216}
    217
    218static const struct w1_family_ops w1_eprom_fops = {
    219	.add_slave	= w1_eprom_add_slave,
    220};
    221
    222static struct w1_family w1_family_09 = {
    223	.fid = W1_DS2502_FAMILY,
    224	.fops = &w1_eprom_fops,
    225};
    226
    227static struct w1_family w1_family_0b = {
    228	.fid = W1_DS2505_FAMILY,
    229	.fops = &w1_eprom_fops,
    230};
    231
    232static struct w1_family w1_family_89 = {
    233	.fid = W1_DS2502_UNW_FAMILY,
    234	.fops = &w1_eprom_fops,
    235};
    236
    237static struct w1_family w1_family_91 = {
    238	.fid = W1_DS2501_UNW_FAMILY,
    239	.fops = &w1_eprom_fops,
    240};
    241
    242static int __init w1_ds250x_init(void)
    243{
    244	int err;
    245
    246	err = w1_register_family(&w1_family_09);
    247	if (err)
    248		return err;
    249
    250	err = w1_register_family(&w1_family_0b);
    251	if (err)
    252		goto err_0b;
    253
    254	err = w1_register_family(&w1_family_89);
    255	if (err)
    256		goto err_89;
    257
    258	err = w1_register_family(&w1_family_91);
    259	if (err)
    260		goto err_91;
    261
    262	return 0;
    263
    264err_91:
    265	w1_unregister_family(&w1_family_89);
    266err_89:
    267	w1_unregister_family(&w1_family_0b);
    268err_0b:
    269	w1_unregister_family(&w1_family_09);
    270	return err;
    271}
    272
    273static void __exit w1_ds250x_exit(void)
    274{
    275	w1_unregister_family(&w1_family_09);
    276	w1_unregister_family(&w1_family_0b);
    277	w1_unregister_family(&w1_family_89);
    278	w1_unregister_family(&w1_family_91);
    279}
    280
    281module_init(w1_ds250x_init);
    282module_exit(w1_ds250x_exit);
    283
    284MODULE_AUTHOR("Thomas Bogendoerfer <tbogendoerfe@suse.de>");
    285MODULE_DESCRIPTION("w1 family driver for DS250x Add Only Memory");
    286MODULE_LICENSE("GPL");
    287MODULE_ALIAS("w1-family-" __stringify(W1_DS2502_FAMILY));
    288MODULE_ALIAS("w1-family-" __stringify(W1_DS2505_FAMILY));
    289MODULE_ALIAS("w1-family-" __stringify(W1_DS2501_UNW_FAMILY));
    290MODULE_ALIAS("w1-family-" __stringify(W1_DS2502_UNW_FAMILY));