blob: 31cc871330cacfdaae04cc9f778533c2acab5ff9 [file] [log] [blame]
Stefan Boschb4bb31d2020-07-10 19:07:37 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd.
4 * (http://www.friendlyarm.com)
5 */
6
7#include <config.h>
Stefan Boschb4bb31d2020-07-10 19:07:37 +02008#include <errno.h>
9#include <asm/io.h>
10#include <asm/arch/clk.h>
Tom Rini66faa432022-12-04 10:03:26 -050011#include <asm/arch/pwm.h>
Stefan Boschb4bb31d2020-07-10 19:07:37 +020012#include <i2c.h>
Igor Prusovc3421ea2023-11-09 20:10:04 +030013#include <linux/time.h>
Stefan Boschb4bb31d2020-07-10 19:07:37 +020014
15#include <irq_func.h>
16
17#include <asm/arch/nexell.h>
18#include <asm/arch/nx_gpio.h>
19
Stefan Boschb4bb31d2020-07-10 19:07:37 +020020#define SAMPLE_BPS 9600
21#define SAMPLE_IN_US 101 /* (1000000 / BPS) */
22
23#define REQ_INFO 0x60U
24#define REQ_BL 0x80U
25
26#define BUS_I2C 0x18
27#define ONEWIRE_I2C_BUS 2
28#define ONEWIRE_I2C_ADDR 0x2f
29
30static int bus_type = -1;
31static int lcd_id = -1;
32static unsigned short lcd_fwrev;
33static int current_brightness = -1;
Igor Opaniukf7c91762021-02-09 13:52:45 +020034#if CONFIG_IS_ENABLED(DM_I2C)
Stefan Boschb4bb31d2020-07-10 19:07:37 +020035static struct udevice *i2c_dev;
36#endif
37
38/* debug */
39#if (0)
40#define DBGOUT(msg...) do { printf("onewire: " msg); } while (0)
41#else
42#define DBGOUT(msg...) do {} while (0)
43#endif
44
45/* based on web page from http://lfh1986.blogspot.com */
46static unsigned char crc8_ow(unsigned int v, unsigned int len)
47{
48 unsigned char crc = 0xACU;
49
50 while (len--) {
51 if ((crc & 0x80U) != 0) {
52 crc <<= 1;
53 crc ^= 0x7U;
54 } else {
55 crc <<= 1;
56 }
57 if ((v & (1U << 31)) != 0)
58 crc ^= 0x7U;
59 v <<= 1;
60 }
61 return crc;
62}
63
64/* GPIO helpers */
65#define __IO_GRP 2 /* GPIOC15 */
66#define __IO_IDX 15
67
68static inline void set_pin_as_input(void)
69{
70 nx_gpio_set_output_enable(__IO_GRP, __IO_IDX, 0);
71}
72
73static inline void set_pin_as_output(void)
74{
75 nx_gpio_set_output_enable(__IO_GRP, __IO_IDX, 1);
76}
77
78static inline void set_pin_value(int v)
79{
80 nx_gpio_set_output_value(__IO_GRP, __IO_IDX, !!v);
81}
82
83static inline int get_pin_value(void)
84{
85 return nx_gpio_get_input_value(__IO_GRP, __IO_IDX);
86}
87
88/* Timer helpers */
89#define PWM_CH 3
90#define PWM_TCON (PHY_BASEADDR_PWM + 0x08)
91#define PWM_TCON_START (1 << 16)
92#define PWM_TINT_CSTAT (PHY_BASEADDR_PWM + 0x44)
93
94static int onewire_init_timer(void)
95{
96 int period_ns = NSEC_PER_SEC / SAMPLE_BPS;
97
98 /* range: 1080~1970 */
99 period_ns -= 1525;
100
Tom Rini66faa432022-12-04 10:03:26 -0500101 return s5p_pwm_config(PWM_CH, period_ns >> 1, period_ns);
Stefan Boschb4bb31d2020-07-10 19:07:37 +0200102}
103
104static void wait_one_tick(void)
105{
106 unsigned int tcon;
107
108 tcon = readl(PWM_TCON);
109 tcon |= PWM_TCON_START;
110 writel(tcon, PWM_TCON);
111
112 while (1) {
113 if (readl(PWM_TINT_CSTAT) & (1 << (5 + PWM_CH)))
114 break;
115 }
116
117 writel((1 << (5 + PWM_CH)), PWM_TINT_CSTAT);
118
119 tcon &= ~PWM_TCON_START;
120 writel(tcon, PWM_TCON);
121}
122
123/* Session handler */
124static int onewire_session(unsigned char req, unsigned char res[])
125{
126 unsigned int Req;
127 unsigned int *Res;
128 int ints = disable_interrupts();
129 int i;
130 int ret;
131
132 Req = (req << 24) | (crc8_ow(req << 24, 8) << 16);
133 Res = (unsigned int *)res;
134
135 set_pin_value(1);
136 set_pin_as_output();
137 for (i = 0; i < 60; i++)
138 wait_one_tick();
139
140 set_pin_value(0);
141 for (i = 0; i < 2; i++)
142 wait_one_tick();
143
144 for (i = 0; i < 16; i++) {
145 int v = !!(Req & (1U << 31));
146
147 Req <<= 1;
148 set_pin_value(v);
149 wait_one_tick();
150 }
151
152 wait_one_tick();
153 set_pin_as_input();
154 wait_one_tick();
155 for (i = 0; i < 32; i++) {
156 (*Res) <<= 1;
157 (*Res) |= get_pin_value();
158 wait_one_tick();
159 }
160 set_pin_value(1);
161 set_pin_as_output();
162
163 if (ints)
164 enable_interrupts();
165
166 ret = crc8_ow(*Res, 24) == res[0];
167 DBGOUT("req = %02X, res = %02X%02X%02X%02X, ret = %d\n",
168 req, res[3], res[2], res[1], res[0], ret);
169
170 return ret;
171}
172
173static int onewire_i2c_do_request(unsigned char req, unsigned char *buf)
174{
175 unsigned char tx[4];
176 int ret;
177
178 tx[0] = req;
179 tx[1] = crc8_ow(req << 24, 8);
180
Igor Opaniukf7c91762021-02-09 13:52:45 +0200181#if CONFIG_IS_ENABLED(DM_I2C)
Stefan Boschb4bb31d2020-07-10 19:07:37 +0200182 if (dm_i2c_write(i2c_dev, 0, tx, 2))
183 return -EIO;
184
185 if (!buf)
186 return 0;
187
188 if (dm_i2c_read(i2c_dev, 0, buf, 4))
189 return -EIO;
190#else
191 if (i2c_write(ONEWIRE_I2C_ADDR, 0, 0, tx, 2))
192 return -EIO;
193
194 if (!buf) /* NO READ */
195 return 0;
196
197 if (i2c_read(ONEWIRE_I2C_ADDR, 0, 0, buf, 4))
198 return -EIO;
199#endif
200
201 ret = crc8_ow((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8), 24);
202 DBGOUT("req = %02X, res = %02X%02X%02X%02X, ret = %02x\n",
203 req, buf[0], buf[1], buf[2], buf[3], ret);
204
205 return (ret == buf[3]) ? 0 : -EIO;
206}
207
208static void onewire_i2c_init(void)
209{
210 unsigned char buf[4];
211 int ret;
212
Igor Opaniukf7c91762021-02-09 13:52:45 +0200213#if CONFIG_IS_ENABLED(DM_I2C)
Stefan Boschb4bb31d2020-07-10 19:07:37 +0200214 ret = i2c_get_chip_for_busnum(ONEWIRE_I2C_BUS,
215 ONEWIRE_I2C_ADDR, 0, &i2c_dev);
216#else
217 i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
218 i2c_set_bus_num(ONEWIRE_I2C_BUS);
219
220 ret = i2c_probe(ONEWIRE_I2C_ADDR);
221#endif
222 if (ret)
223 return;
224
225 ret = onewire_i2c_do_request(REQ_INFO, buf);
226 if (!ret) {
227 lcd_id = buf[0];
228 lcd_fwrev = buf[1] * 0x100 + buf[2];
229 bus_type = BUS_I2C;
230 }
231}
232
233void onewire_init(void)
234{
235 /* GPIO, Pull-off */
236 nx_gpio_set_pad_function(__IO_GRP, __IO_IDX, 1);
237 nx_gpio_set_pull_mode(__IO_GRP, __IO_IDX, 2);
238
239 onewire_init_timer();
240 onewire_i2c_init();
241}
242
243int onewire_get_info(unsigned char *lcd, unsigned short *fw_ver)
244{
245 unsigned char res[4];
246 int i;
247
248 if (bus_type == BUS_I2C && lcd_id > 0) {
249 *lcd = lcd_id;
250 *fw_ver = lcd_fwrev;
251 return 0;
252 }
253
254 for (i = 0; i < 3; i++) {
255 if (onewire_session(REQ_INFO, res)) {
256 *lcd = res[3];
257 *fw_ver = res[2] * 0x100 + res[1];
258 lcd_id = *lcd;
259 DBGOUT("lcd = %d, fw_ver = %x\n", *lcd, *fw_ver);
260 return 0;
261 }
262 }
263
264 /* LCD unknown or not connected */
265 *lcd = 0;
266 *fw_ver = -1;
267
268 return -1;
269}
270
271int onewire_get_lcd_id(void)
272{
273 return lcd_id;
274}
275
276int onewire_set_backlight(int brightness)
277{
278 unsigned char res[4];
279 int i;
280
281 if (brightness == current_brightness)
282 return 0;
283
284 if (brightness > 127)
285 brightness = 127;
286 else if (brightness < 0)
287 brightness = 0;
288
289 if (bus_type == BUS_I2C) {
290 onewire_i2c_do_request((REQ_BL | brightness), NULL);
291 current_brightness = brightness;
292 return 0;
293 }
294
295 for (i = 0; i < 3; i++) {
296 if (onewire_session((REQ_BL | brightness), res)) {
297 current_brightness = brightness;
298 return 0;
299 }
300 }
301
302 return -1;
303}