308 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			308 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C
		
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * Copyright (C) Guangzhou FriendlyARM Computer Tech. Co., Ltd.
 | |
|  * (http://www.friendlyarm.com)
 | |
|  */
 | |
| 
 | |
| #include <config.h>
 | |
| #include <common.h>
 | |
| #include <errno.h>
 | |
| #include <asm/io.h>
 | |
| #include <asm/arch/clk.h>
 | |
| #include <i2c.h>
 | |
| #include <pwm.h>
 | |
| 
 | |
| #include <irq_func.h>
 | |
| 
 | |
| #include <asm/arch/nexell.h>
 | |
| #include <asm/arch/nx_gpio.h>
 | |
| 
 | |
| #ifndef NSEC_PER_SEC
 | |
| #define NSEC_PER_SEC	1000000000L
 | |
| #endif
 | |
| 
 | |
| #define SAMPLE_BPS		9600
 | |
| #define SAMPLE_IN_US	101		/* (1000000 / BPS) */
 | |
| 
 | |
| #define REQ_INFO		0x60U
 | |
| #define REQ_BL			0x80U
 | |
| 
 | |
| #define BUS_I2C			0x18
 | |
| #define ONEWIRE_I2C_BUS		2
 | |
| #define ONEWIRE_I2C_ADDR	0x2f
 | |
| 
 | |
| static int bus_type = -1;
 | |
| static int lcd_id = -1;
 | |
| static unsigned short lcd_fwrev;
 | |
| static int current_brightness = -1;
 | |
| #if CONFIG_IS_ENABLED(DM_I2C)
 | |
| static struct udevice *i2c_dev;
 | |
| #endif
 | |
| 
 | |
| /* debug */
 | |
| #if (0)
 | |
| #define DBGOUT(msg...)	do { printf("onewire: " msg); } while (0)
 | |
| #else
 | |
| #define DBGOUT(msg...)	do {} while (0)
 | |
| #endif
 | |
| 
 | |
| /* based on web page from http://lfh1986.blogspot.com */
 | |
| static unsigned char crc8_ow(unsigned int v, unsigned int len)
 | |
| {
 | |
| 	unsigned char crc = 0xACU;
 | |
| 
 | |
| 	while (len--) {
 | |
| 		if ((crc & 0x80U) != 0) {
 | |
| 			crc <<= 1;
 | |
| 			crc ^= 0x7U;
 | |
| 		} else {
 | |
| 			crc <<= 1;
 | |
| 		}
 | |
| 		if ((v & (1U << 31)) != 0)
 | |
| 			crc ^= 0x7U;
 | |
| 		v <<= 1;
 | |
| 	}
 | |
| 	return crc;
 | |
| }
 | |
| 
 | |
| /* GPIO helpers */
 | |
| #define __IO_GRP		2	/* GPIOC15 */
 | |
| #define __IO_IDX		15
 | |
| 
 | |
| static inline void set_pin_as_input(void)
 | |
| {
 | |
| 	nx_gpio_set_output_enable(__IO_GRP, __IO_IDX, 0);
 | |
| }
 | |
| 
 | |
| static inline void set_pin_as_output(void)
 | |
| {
 | |
| 	nx_gpio_set_output_enable(__IO_GRP, __IO_IDX, 1);
 | |
| }
 | |
| 
 | |
| static inline void set_pin_value(int v)
 | |
| {
 | |
| 	nx_gpio_set_output_value(__IO_GRP, __IO_IDX, !!v);
 | |
| }
 | |
| 
 | |
| static inline int get_pin_value(void)
 | |
| {
 | |
| 	return nx_gpio_get_input_value(__IO_GRP, __IO_IDX);
 | |
| }
 | |
| 
 | |
| /* Timer helpers */
 | |
| #define PWM_CH				3
 | |
| #define PWM_TCON			(PHY_BASEADDR_PWM + 0x08)
 | |
| #define PWM_TCON_START		(1 << 16)
 | |
| #define PWM_TINT_CSTAT		(PHY_BASEADDR_PWM + 0x44)
 | |
| 
 | |
| static int onewire_init_timer(void)
 | |
| {
 | |
| 	int period_ns = NSEC_PER_SEC / SAMPLE_BPS;
 | |
| 
 | |
| 	/* range: 1080~1970 */
 | |
| 	period_ns -= 1525;
 | |
| 
 | |
| 	return pwm_config(PWM_CH, period_ns >> 1, period_ns);
 | |
| }
 | |
| 
 | |
| static void wait_one_tick(void)
 | |
| {
 | |
| 	unsigned int tcon;
 | |
| 
 | |
| 	tcon = readl(PWM_TCON);
 | |
| 	tcon |= PWM_TCON_START;
 | |
| 	writel(tcon, PWM_TCON);
 | |
| 
 | |
| 	while (1) {
 | |
| 		if (readl(PWM_TINT_CSTAT) & (1 << (5 + PWM_CH)))
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	writel((1 << (5 + PWM_CH)), PWM_TINT_CSTAT);
 | |
| 
 | |
| 	tcon &= ~PWM_TCON_START;
 | |
| 	writel(tcon, PWM_TCON);
 | |
| }
 | |
| 
 | |
| /* Session handler */
 | |
| static int onewire_session(unsigned char req, unsigned char res[])
 | |
| {
 | |
| 	unsigned int Req;
 | |
| 	unsigned int *Res;
 | |
| 	int ints = disable_interrupts();
 | |
| 	int i;
 | |
| 	int ret;
 | |
| 
 | |
| 	Req = (req << 24) | (crc8_ow(req << 24, 8) << 16);
 | |
| 	Res = (unsigned int *)res;
 | |
| 
 | |
| 	set_pin_value(1);
 | |
| 	set_pin_as_output();
 | |
| 	for (i = 0; i < 60; i++)
 | |
| 		wait_one_tick();
 | |
| 
 | |
| 	set_pin_value(0);
 | |
| 	for (i = 0; i < 2; i++)
 | |
| 		wait_one_tick();
 | |
| 
 | |
| 	for (i = 0; i < 16; i++) {
 | |
| 		int v = !!(Req & (1U << 31));
 | |
| 
 | |
| 		Req <<= 1;
 | |
| 		set_pin_value(v);
 | |
| 		wait_one_tick();
 | |
| 	}
 | |
| 
 | |
| 	wait_one_tick();
 | |
| 	set_pin_as_input();
 | |
| 	wait_one_tick();
 | |
| 	for (i = 0; i < 32; i++) {
 | |
| 		(*Res) <<= 1;
 | |
| 		(*Res) |= get_pin_value();
 | |
| 		wait_one_tick();
 | |
| 	}
 | |
| 	set_pin_value(1);
 | |
| 	set_pin_as_output();
 | |
| 
 | |
| 	if (ints)
 | |
| 		enable_interrupts();
 | |
| 
 | |
| 	ret = crc8_ow(*Res, 24) == res[0];
 | |
| 	DBGOUT("req = %02X, res = %02X%02X%02X%02X, ret = %d\n",
 | |
| 	       req, res[3], res[2], res[1], res[0], ret);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int onewire_i2c_do_request(unsigned char req, unsigned char *buf)
 | |
| {
 | |
| 	unsigned char tx[4];
 | |
| 	int ret;
 | |
| 
 | |
| 	tx[0] = req;
 | |
| 	tx[1] = crc8_ow(req << 24, 8);
 | |
| 
 | |
| #if CONFIG_IS_ENABLED(DM_I2C)
 | |
| 	if (dm_i2c_write(i2c_dev, 0, tx, 2))
 | |
| 		return -EIO;
 | |
| 
 | |
| 	if (!buf)
 | |
| 		return 0;
 | |
| 
 | |
| 	if (dm_i2c_read(i2c_dev, 0, buf, 4))
 | |
| 		return -EIO;
 | |
| #else
 | |
| 	if (i2c_write(ONEWIRE_I2C_ADDR, 0, 0, tx, 2))
 | |
| 		return -EIO;
 | |
| 
 | |
| 	if (!buf) /* NO READ */
 | |
| 		return 0;
 | |
| 
 | |
| 	if (i2c_read(ONEWIRE_I2C_ADDR, 0, 0, buf, 4))
 | |
| 		return -EIO;
 | |
| #endif
 | |
| 
 | |
| 	ret = crc8_ow((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8), 24);
 | |
| 	DBGOUT("req = %02X, res = %02X%02X%02X%02X, ret = %02x\n",
 | |
| 	       req, buf[0], buf[1], buf[2], buf[3], ret);
 | |
| 
 | |
| 	return (ret == buf[3]) ? 0 : -EIO;
 | |
| }
 | |
| 
 | |
| static void onewire_i2c_init(void)
 | |
| {
 | |
| 	unsigned char buf[4];
 | |
| 	int ret;
 | |
| 
 | |
| #if CONFIG_IS_ENABLED(DM_I2C)
 | |
| 	ret = i2c_get_chip_for_busnum(ONEWIRE_I2C_BUS,
 | |
| 				      ONEWIRE_I2C_ADDR, 0, &i2c_dev);
 | |
| #else
 | |
| 	i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
 | |
| 	i2c_set_bus_num(ONEWIRE_I2C_BUS);
 | |
| 
 | |
| 	ret = i2c_probe(ONEWIRE_I2C_ADDR);
 | |
| #endif
 | |
| 	if (ret)
 | |
| 		return;
 | |
| 
 | |
| 	ret = onewire_i2c_do_request(REQ_INFO, buf);
 | |
| 	if (!ret) {
 | |
| 		lcd_id = buf[0];
 | |
| 		lcd_fwrev = buf[1] * 0x100 + buf[2];
 | |
| 		bus_type = BUS_I2C;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void onewire_init(void)
 | |
| {
 | |
| 	/* GPIO, Pull-off */
 | |
| 	nx_gpio_set_pad_function(__IO_GRP, __IO_IDX, 1);
 | |
| 	nx_gpio_set_pull_mode(__IO_GRP, __IO_IDX, 2);
 | |
| 
 | |
| 	onewire_init_timer();
 | |
| 	onewire_i2c_init();
 | |
| }
 | |
| 
 | |
| int onewire_get_info(unsigned char *lcd, unsigned short *fw_ver)
 | |
| {
 | |
| 	unsigned char res[4];
 | |
| 	int i;
 | |
| 
 | |
| 	if (bus_type == BUS_I2C && lcd_id > 0) {
 | |
| 		*lcd = lcd_id;
 | |
| 		*fw_ver = lcd_fwrev;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < 3; i++) {
 | |
| 		if (onewire_session(REQ_INFO, res)) {
 | |
| 			*lcd = res[3];
 | |
| 			*fw_ver = res[2] * 0x100 + res[1];
 | |
| 			lcd_id = *lcd;
 | |
| 			DBGOUT("lcd = %d, fw_ver = %x\n", *lcd, *fw_ver);
 | |
| 			return 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* LCD unknown or not connected */
 | |
| 	*lcd = 0;
 | |
| 	*fw_ver = -1;
 | |
| 
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| int onewire_get_lcd_id(void)
 | |
| {
 | |
| 	return lcd_id;
 | |
| }
 | |
| 
 | |
| int onewire_set_backlight(int brightness)
 | |
| {
 | |
| 	unsigned char res[4];
 | |
| 	int i;
 | |
| 
 | |
| 	if (brightness == current_brightness)
 | |
| 		return 0;
 | |
| 
 | |
| 	if (brightness > 127)
 | |
| 		brightness = 127;
 | |
| 	else if (brightness < 0)
 | |
| 		brightness = 0;
 | |
| 
 | |
| 	if (bus_type == BUS_I2C) {
 | |
| 		onewire_i2c_do_request((REQ_BL | brightness), NULL);
 | |
| 		current_brightness = brightness;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < 3; i++) {
 | |
| 		if (onewire_session((REQ_BL | brightness), res)) {
 | |
| 			current_brightness = brightness;
 | |
| 			return 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return -1;
 | |
| }
 |