// 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; }