162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com>
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Original author:
662306a36Sopenharmony_ci * Ben Collins <bcollins@ubuntu.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Additional work by:
962306a36Sopenharmony_ci * John Brooks <john.brooks@bluecherry.net>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "solo6x10.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* Control */
1862306a36Sopenharmony_ci#define EE_SHIFT_CLK	0x04
1962306a36Sopenharmony_ci#define EE_CS		0x08
2062306a36Sopenharmony_ci#define EE_DATA_WRITE	0x02
2162306a36Sopenharmony_ci#define EE_DATA_READ	0x01
2262306a36Sopenharmony_ci#define EE_ENB		(0x80 | EE_CS)
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define eeprom_delay()	udelay(100)
2562306a36Sopenharmony_ci#if 0
2662306a36Sopenharmony_ci#define eeprom_delay()	solo_reg_read(solo_dev, SOLO_EEPROM_CTRL)
2762306a36Sopenharmony_ci#define eeprom_delay()	({				\
2862306a36Sopenharmony_ci	int i, ret;					\
2962306a36Sopenharmony_ci	udelay(100);					\
3062306a36Sopenharmony_ci	for (i = ret = 0; i < 1000 && !ret; i++)	\
3162306a36Sopenharmony_ci		ret = solo_eeprom_reg_read(solo_dev);	\
3262306a36Sopenharmony_ci})
3362306a36Sopenharmony_ci#endif
3462306a36Sopenharmony_ci#define ADDR_LEN	6
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* Commands */
3762306a36Sopenharmony_ci#define EE_EWEN_CMD	4
3862306a36Sopenharmony_ci#define EE_EWDS_CMD	4
3962306a36Sopenharmony_ci#define EE_WRITE_CMD	5
4062306a36Sopenharmony_ci#define EE_READ_CMD	6
4162306a36Sopenharmony_ci#define EE_ERASE_CMD	7
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic unsigned int solo_eeprom_reg_read(struct solo_dev *solo_dev)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	return solo_reg_read(solo_dev, SOLO_EEPROM_CTRL) & EE_DATA_READ;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void solo_eeprom_reg_write(struct solo_dev *solo_dev, u32 data)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	solo_reg_write(solo_dev, SOLO_EEPROM_CTRL, data);
5162306a36Sopenharmony_ci	eeprom_delay();
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic void solo_eeprom_cmd(struct solo_dev *solo_dev, int cmd)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	int i;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ACCESS_EN);
5962306a36Sopenharmony_ci	solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	for (i = 4 + ADDR_LEN; i >= 0; i--) {
6262306a36Sopenharmony_ci		int dataval = (cmd & (1 << i)) ? EE_DATA_WRITE : 0;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci		solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | dataval);
6562306a36Sopenharmony_ci		solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE |
6662306a36Sopenharmony_ci				      EE_SHIFT_CLK | dataval);
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ciunsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	int ewen_cmd = (w_en ? 0x3f : 0) | (EE_EWEN_CMD << ADDR_LEN);
7562306a36Sopenharmony_ci	unsigned int retval = 0;
7662306a36Sopenharmony_ci	int i;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	solo_eeprom_cmd(solo_dev, ewen_cmd);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	for (i = 0; i < 16; i++) {
8162306a36Sopenharmony_ci		solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE |
8262306a36Sopenharmony_ci				      EE_SHIFT_CLK);
8362306a36Sopenharmony_ci		retval = (retval << 1) | solo_eeprom_reg_read(solo_dev);
8462306a36Sopenharmony_ci		solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE);
8562306a36Sopenharmony_ci		retval = (retval << 1) | solo_eeprom_reg_read(solo_dev);
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	solo_eeprom_reg_write(solo_dev, ~EE_CS);
8962306a36Sopenharmony_ci	retval = (retval << 1) | solo_eeprom_reg_read(solo_dev);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return retval;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci__be16 solo_eeprom_read(struct solo_dev *solo_dev, int loc)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	int read_cmd = loc | (EE_READ_CMD << ADDR_LEN);
9762306a36Sopenharmony_ci	u16 retval = 0;
9862306a36Sopenharmony_ci	int i;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	solo_eeprom_cmd(solo_dev, read_cmd);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	for (i = 0; i < 16; i++) {
10362306a36Sopenharmony_ci		solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE |
10462306a36Sopenharmony_ci				      EE_SHIFT_CLK);
10562306a36Sopenharmony_ci		retval = (retval << 1) | solo_eeprom_reg_read(solo_dev);
10662306a36Sopenharmony_ci		solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE);
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	solo_eeprom_reg_write(solo_dev, ~EE_CS);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return (__force __be16)retval;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ciint solo_eeprom_write(struct solo_dev *solo_dev, int loc,
11562306a36Sopenharmony_ci		      __be16 data)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	int write_cmd = loc | (EE_WRITE_CMD << ADDR_LEN);
11862306a36Sopenharmony_ci	unsigned int retval;
11962306a36Sopenharmony_ci	int i;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	solo_eeprom_cmd(solo_dev, write_cmd);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	for (i = 15; i >= 0; i--) {
12462306a36Sopenharmony_ci		unsigned int dataval = ((__force unsigned)data >> i) & 1;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci		solo_eeprom_reg_write(solo_dev, EE_ENB);
12762306a36Sopenharmony_ci		solo_eeprom_reg_write(solo_dev,
12862306a36Sopenharmony_ci				      EE_ENB | (dataval << 1) | EE_SHIFT_CLK);
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	solo_eeprom_reg_write(solo_dev, EE_ENB);
13262306a36Sopenharmony_ci	solo_eeprom_reg_write(solo_dev, ~EE_CS);
13362306a36Sopenharmony_ci	solo_eeprom_reg_write(solo_dev, EE_ENB);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	for (i = retval = 0; i < 10000 && !retval; i++)
13662306a36Sopenharmony_ci		retval = solo_eeprom_reg_read(solo_dev);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	solo_eeprom_reg_write(solo_dev, ~EE_CS);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return !retval;
14162306a36Sopenharmony_ci}
142