1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2010-2013 Bluecherry, LLC <https://www.bluecherrydvr.com>
4 *
5 * Original author:
6 * Ben Collins <bcollins@ubuntu.com>
7 *
8 * Additional work by:
9 * John Brooks <john.brooks@bluecherry.net>
10 */
11
12#include <linux/kernel.h>
13#include <linux/delay.h>
14
15#include "solo6x10.h"
16
17/* Control */
18#define EE_SHIFT_CLK	0x04
19#define EE_CS		0x08
20#define EE_DATA_WRITE	0x02
21#define EE_DATA_READ	0x01
22#define EE_ENB		(0x80 | EE_CS)
23
24#define eeprom_delay()	udelay(100)
25#if 0
26#define eeprom_delay()	solo_reg_read(solo_dev, SOLO_EEPROM_CTRL)
27#define eeprom_delay()	({				\
28	int i, ret;					\
29	udelay(100);					\
30	for (i = ret = 0; i < 1000 && !ret; i++)	\
31		ret = solo_eeprom_reg_read(solo_dev);	\
32})
33#endif
34#define ADDR_LEN	6
35
36/* Commands */
37#define EE_EWEN_CMD	4
38#define EE_EWDS_CMD	4
39#define EE_WRITE_CMD	5
40#define EE_READ_CMD	6
41#define EE_ERASE_CMD	7
42
43static unsigned int solo_eeprom_reg_read(struct solo_dev *solo_dev)
44{
45	return solo_reg_read(solo_dev, SOLO_EEPROM_CTRL) & EE_DATA_READ;
46}
47
48static void solo_eeprom_reg_write(struct solo_dev *solo_dev, u32 data)
49{
50	solo_reg_write(solo_dev, SOLO_EEPROM_CTRL, data);
51	eeprom_delay();
52}
53
54static void solo_eeprom_cmd(struct solo_dev *solo_dev, int cmd)
55{
56	int i;
57
58	solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ACCESS_EN);
59	solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE);
60
61	for (i = 4 + ADDR_LEN; i >= 0; i--) {
62		int dataval = (cmd & (1 << i)) ? EE_DATA_WRITE : 0;
63
64		solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE | dataval);
65		solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE |
66				      EE_SHIFT_CLK | dataval);
67	}
68
69	solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE);
70}
71
72unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en)
73{
74	int ewen_cmd = (w_en ? 0x3f : 0) | (EE_EWEN_CMD << ADDR_LEN);
75	unsigned int retval = 0;
76	int i;
77
78	solo_eeprom_cmd(solo_dev, ewen_cmd);
79
80	for (i = 0; i < 16; i++) {
81		solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE |
82				      EE_SHIFT_CLK);
83		retval = (retval << 1) | solo_eeprom_reg_read(solo_dev);
84		solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE);
85		retval = (retval << 1) | solo_eeprom_reg_read(solo_dev);
86	}
87
88	solo_eeprom_reg_write(solo_dev, ~EE_CS);
89	retval = (retval << 1) | solo_eeprom_reg_read(solo_dev);
90
91	return retval;
92}
93
94__be16 solo_eeprom_read(struct solo_dev *solo_dev, int loc)
95{
96	int read_cmd = loc | (EE_READ_CMD << ADDR_LEN);
97	u16 retval = 0;
98	int i;
99
100	solo_eeprom_cmd(solo_dev, read_cmd);
101
102	for (i = 0; i < 16; i++) {
103		solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE |
104				      EE_SHIFT_CLK);
105		retval = (retval << 1) | solo_eeprom_reg_read(solo_dev);
106		solo_eeprom_reg_write(solo_dev, SOLO_EEPROM_ENABLE);
107	}
108
109	solo_eeprom_reg_write(solo_dev, ~EE_CS);
110
111	return (__force __be16)retval;
112}
113
114int solo_eeprom_write(struct solo_dev *solo_dev, int loc,
115		      __be16 data)
116{
117	int write_cmd = loc | (EE_WRITE_CMD << ADDR_LEN);
118	unsigned int retval;
119	int i;
120
121	solo_eeprom_cmd(solo_dev, write_cmd);
122
123	for (i = 15; i >= 0; i--) {
124		unsigned int dataval = ((__force unsigned)data >> i) & 1;
125
126		solo_eeprom_reg_write(solo_dev, EE_ENB);
127		solo_eeprom_reg_write(solo_dev,
128				      EE_ENB | (dataval << 1) | EE_SHIFT_CLK);
129	}
130
131	solo_eeprom_reg_write(solo_dev, EE_ENB);
132	solo_eeprom_reg_write(solo_dev, ~EE_CS);
133	solo_eeprom_reg_write(solo_dev, EE_ENB);
134
135	for (i = retval = 0; i < 10000 && !retval; i++)
136		retval = solo_eeprom_reg_read(solo_dev);
137
138	solo_eeprom_reg_write(solo_dev, ~EE_CS);
139
140	return !retval;
141}
142