Message ID | 1398561270-25091-4-git-send-email-danindrey@mail.ru |
---|---|
State | Deferred |
Delegated to: | Tom Warren |
Headers | show |
Hello Andrey, Am 27.04.2014 03:14, schrieb Andrey Danin: > Signed-off-by: Andrey Danin<danindrey@mail.ru> > CC: Stephen Warren<swarren@nvidia.com> > CC: Marc Dietrich<marvin24@gmx.de> > CC: Julian Andres Klode<jak@jak-linux.org> > CC: ac100@lists.launchpad.net > --- > Changes for v2: > - NVEC driver was reworked to use tegra-i2c > > arch/arm/include/asm/arch-tegra/tegra_nvec.h | 130 ++++++++++++ > board/nvidia/common/board.c | 12 ++ > drivers/i2c/Makefile | 1 + > drivers/i2c/tegra_nvec.c | 294 ++++++++++++++++++++++++++ > include/fdtdec.h | 1 + > lib/fdtdec.c | 1 + > 6 files changed, 439 insertions(+) > create mode 100644 arch/arm/include/asm/arch-tegra/tegra_nvec.h > create mode 100644 drivers/i2c/tegra_nvec.c > > diff --git a/arch/arm/include/asm/arch-tegra/tegra_nvec.h b/arch/arm/include/asm/arch-tegra/tegra_nvec.h > new file mode 100644 > index 0000000..f1cec0d > --- /dev/null > +++ b/arch/arm/include/asm/arch-tegra/tegra_nvec.h > @@ -0,0 +1,130 @@ > +/* > + * (C) Copyright 2014 > + * Andrey Danin<danindrey@mail.ru> > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#ifndef _TEGRA_NVEC_H_ > +#define _TEGRA_NVEC_H_ > + > +#define I2C_CNFG 0x00 > +#define I2C_CNFG_PACKET_MODE_EN (1<<10) > +#define I2C_CNFG_NEW_MASTER_SFM (1<<11) > +#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12 > + > +#define I2C_SL_CNFG 0x20 > +#define I2C_SL_NEWSL (1<<2) > +#define I2C_SL_NACK (1<<1) > +#define I2C_SL_RESP (1<<0) > +#define I2C_SL_IRQ (1<<3) > +#define END_TRANS (1<<4) > +#define RCVD (1<<2) > +#define RNW (1<<1) > + > +#define I2C_SL_RCVD 0x24 > +#define I2C_SL_STATUS 0x28 > +#define I2C_SL_ADDR1 0x2c > +#define I2C_SL_ADDR2 0x30 > +#define I2C_SL_DELAY_COUNT 0x3c > + > + please only one line, check globally. [...] > diff --git a/drivers/i2c/tegra_nvec.c b/drivers/i2c/tegra_nvec.c > new file mode 100644 > index 0000000..b568988 > --- /dev/null > +++ b/drivers/i2c/tegra_nvec.c > @@ -0,0 +1,294 @@ > +/* > + * (C) Copyright 2014 > + * Andrey Danin<danindrey@mail.ru> > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include<common.h> > +#include<fdtdec.h> > +#include<i2c.h> > +#include<asm/io.h> > +#include<asm/gpio.h> > +#include<asm/arch/clock.h> > +#include<asm/arch/funcmux.h> > +#include<asm/arch-tegra/tegra_nvec.h> > + > +#ifndef CONFIG_SYS_I2C_TEGRA_NVEC > +#error "You should enable CONFIG_SYS_I2C_TEGRA_NVEC" > +#endif > + > +DECLARE_GLOBAL_DATA_PTR; > + > +struct nvec_periph devices[NVEC_LAST_MSG]; > + > + > +struct nvec_t { > + int i2c_bus; > + struct fdt_gpio_state gpio; > +} nvec_data; > + > + > +/* nvec commands */ > +char noop[] = { NVEC_CNTL, CNTL_NOOP }; > + > +void nvec_signal_request(void) > +{ > + gpio_set_value(nvec_data.gpio.gpio, 0); > +} > + > + > +void nvec_clear_request(void) > +{ > + gpio_set_value(nvec_data.gpio.gpio, 1); > +} > + > + > +int nvec_msg_is_event(const unsigned char *msg) > +{ > + return msg[0]>> 7; > +} > + > + > +int nvec_msg_event_type(const unsigned char *msg) > +{ > + return msg[0]& 0x0f; > +} > + > + > +/** > + * Process incoming io message. > + * If message is keyboard event then key code will > + * be added to keys buffer. > + * > + * @param nvec nvec state struct > + */ > +void nvec_process_msg(struct nvec_t *nvec, struct i2c_transaction *trans) > +{ > + (void) nvec; > + no empty line needed. > + const unsigned char *msg = (const unsigned char *)&trans->rx_buf[1]; > + int event_type; > + > + if (!nvec_msg_is_event(msg)) > + return; > + > + event_type = nvec_msg_event_type(msg); > + > + if (event_type< NVEC_KEYBOARD || event_type>= NVEC_LAST_MSG) > + return; > + > + if (devices[event_type].process_msg) > + devices[event_type].process_msg(msg); > +} > + > + > +/** > + * Perform complete io operation (read or write). > + * NOTE: function will wait NVEC_TIMEOUT_MIN (20ms) > + * before status check to avoid nvec hang. > + * > + * @param nvec nvec state struct > + * @param wait_for_ec if 1(NVEC_WAIT_FOR_EC) operation > + * timeout is NVEC_TIMEOUT_MAX (600ms), > + * otherwise function will return if io > + * is not ready. > + * > + * @return nvec_io_* code > + */ > +int nvec_do_io(struct nvec_t *nvec) > +{ > + static unsigned int prev_timer; > + unsigned int poll_start_ms = get_timer(prev_timer); > + struct i2c_transaction trans; > + int res; > + > + if (poll_start_ms> 30000) { > + if (prev_timer != 0) > + nvec_do_request(noop, sizeof(noop)); > + prev_timer = get_timer(0); > + } > + > + memset(&trans, 0, sizeof(trans)); > + trans.start_timeout = 1; > + trans.timeout = 6000; > + > + res = i2c_slave_io(&trans); > + if (res == 0) { > + nvec_process_msg(nvec,&trans); > + return 0; > + } > + > + if (res != -1) > + debug("Error: i2c slave io failed with code %d\n", res); > + > + return -1; > +} > + > +/** > + * Send request and read response. If write or read failed > + * operation will be repeated NVEC_ATTEMPTS_MAX times. > + * > + * @param buf request data > + * @param size request data size > + * @return 0 if ok, -1 on error > + */ > +int nvec_do_request(char *buf, int size) > +{ > + int res; > + struct i2c_transaction trans; > + int i; > + > + nvec_signal_request(); > + > + /* Request */ > + for (i = 0; i< 10; ++i) { > + memset(&trans, 0, sizeof(trans)); > + trans.start_timeout = 600; > + trans.timeout = 6000; > + > + trans.tx_buf[0] = (char)size; > + memcpy(&trans.tx_buf[1], buf, size); > + trans.tx_size = size + 1; > + > + res = i2c_slave_io(&trans); > + if (res == 0) { > + if (trans.tx_pos == trans.tx_size) > + break; > + > + debug("Request was not sent completely"); > + } else if (res != -1) { > + debug("Unknown error while slave io"); > + } > + } > + nvec_clear_request(); > + if (res != 0) { > + error("nvec failed to perform request\n"); > + return -1; > + } > + > + /* Response */ > + for (i = 0; i< 10; ++i) { > + memset(&trans, 0, sizeof(trans)); > + trans.start_timeout = 600; > + trans.timeout = 6000; > + > + res = i2c_slave_io(&trans); > + if (res == 0) > + break; > + } > + if (res != 0) { > + error("nvec failed to read response\n"); > + return -1; > + } > + > + /* TODO Parse response */ ? Is this not a complete driver? > + > + return 0; > +} > + > + > +/** > + * Decode the nvec information from the fdt. > + * > + * @param blob fdt blob > + * @param nvec nvec device sturct > + * @return 0 if ok, -ve on error What is "-ve" ? > + */ > +static int nvec_decode_config(const void *blob, > + struct nvec_t *nvec) > +{ > + int node, parent; > + int i2c_bus; > + > + node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_NVEC); > + if (node< 0) { > + error("Cannot find NVEC node in fdt\n"); > + return node; > + } > + > + parent = fdt_parent_offset(blob, node); > + if (parent< 0) { > + debug("%s: Cannot find node parent\n", __func__); > + return -1; > + } > + > + i2c_bus = i2c_get_bus_num_fdt(parent); > + if (i2c_bus< 0) > + return -1; > + nvec->i2c_bus = i2c_bus; > + > + if (fdtdec_decode_gpio(blob, node, "request-gpios", > + &nvec->gpio)) { > + error("No NVEC request gpio\n"); > + return -1; > + } > + > + debug("NVEC: i2c:%d, gpio:%s(%u)\n", > + nvec->i2c_bus, nvec->gpio.name, nvec->gpio.gpio); > + return 0; > +} > + > + > +int board_nvec_init(void) > +{ > + int res = 0; > + int i; > + > + if (nvec_decode_config(gd->fdt_blob,&nvec_data)) { > + error("Can't parse NVEC node in device tree\n"); > + return -1; > + } > + > + debug("NVEC initialization...\n"); > + > + res = gpio_request(nvec_data.gpio.gpio, NULL); > + if (res != 0) > + error("NVEC: err, gpio_request\n"); > + res = gpio_direction_output(nvec_data.gpio.gpio, 1); > + if (res != 0) > + error("NVEC: err, gpio_direction\n"); > + res = gpio_set_value(nvec_data.gpio.gpio, 1); > + if (res != 0) > + error("NVEC: err, gpio_set_value\n"); > + udelay(100); > + > + i2c_set_bus_num(nvec_data.i2c_bus); > + > + for (i = NVEC_KEYBOARD; i< NVEC_LAST_MSG; ++i) > + if (devices[i].start) { > + debug("Starting device %d(0x%x)\n", i, i); > + devices[i].start(); > + } > + > + return 1; Why "return 1"? > +} > + > + > +int nvec_read_events(void) > +{ > + int res; > + int cnt = 0; > + > + while (++cnt<= 8) { > + res = nvec_do_io(&nvec_data); > + if (res) > + break; > + > + /* TODO Process nvec communication errors */ Again a "TODO" ? > + } > + > + return cnt; > +} > + > + > +int nvec_register_periph(int msg_type, struct nvec_periph *periph) > +{ > + if (devices[msg_type].start || devices[msg_type].process_msg) { > + error("Device for msg %d already registered\n", msg_type); > + return -1; > + } > + > + devices[msg_type] = *periph; > + return 0; > +} > diff --git a/include/fdtdec.h b/include/fdtdec.h > index 19bab79..7b84335 100644 > --- a/include/fdtdec.h > +++ b/include/fdtdec.h > @@ -64,6 +64,7 @@ enum fdt_compat_id { > COMPAT_NVIDIA_TEGRA20_SDMMC, /* Tegra20 SDMMC controller */ > COMPAT_NVIDIA_TEGRA20_SFLASH, /* Tegra 2 SPI flash controller */ > COMPAT_NVIDIA_TEGRA20_SLINK, /* Tegra 2 SPI SLINK controller */ > + COMPAT_NVIDIA_TEGRA20_NVEC, /* Tegra 2 EC controller */ > COMPAT_NVIDIA_TEGRA114_SPI, /* Tegra 114 SPI controller */ > COMPAT_SMSC_LAN9215, /* SMSC 10/100 Ethernet LAN9215 */ > COMPAT_SAMSUNG_EXYNOS5_SROMC, /* Exynos5 SROMC */ > diff --git a/lib/fdtdec.c b/lib/fdtdec.c > index 1fecab3..82df701 100644 > --- a/lib/fdtdec.c > +++ b/lib/fdtdec.c > @@ -37,6 +37,7 @@ static const char * const compat_names[COMPAT_COUNT] = { > COMPAT(NVIDIA_TEGRA20_SDMMC, "nvidia,tegra20-sdhci"), > COMPAT(NVIDIA_TEGRA20_SFLASH, "nvidia,tegra20-sflash"), > COMPAT(NVIDIA_TEGRA20_SLINK, "nvidia,tegra20-slink"), > + COMPAT(NVIDIA_TEGRA20_NVEC, "nvidia,tegra20-nvec"), > COMPAT(NVIDIA_TEGRA114_SPI, "nvidia,tegra114-spi"), > COMPAT(SMSC_LAN9215, "smsc,lan9215"), > COMPAT(SAMSUNG_EXYNOS5_SROMC, "samsung,exynos-sromc"), bye, Heiko
On 29.04.2014 9:43, Heiko Schocher wrote: > Hello Andrey, > Hello Heiko. First of all, thank you for the review! > Am 27.04.2014 03:14, schrieb Andrey Danin: >> Signed-off-by: Andrey Danin<danindrey@mail.ru> >> CC: Stephen Warren<swarren@nvidia.com> >> CC: Marc Dietrich<marvin24@gmx.de> >> CC: Julian Andres Klode<jak@jak-linux.org> >> CC: ac100@lists.launchpad.net >> --- >> Changes for v2: >> - NVEC driver was reworked to use tegra-i2c >> >> arch/arm/include/asm/arch-tegra/tegra_nvec.h | 130 ++++++++++++ >> board/nvidia/common/board.c | 12 ++ >> drivers/i2c/Makefile | 1 + >> drivers/i2c/tegra_nvec.c | 294 >> ++++++++++++++++++++++++++ >> include/fdtdec.h | 1 + >> lib/fdtdec.c | 1 + >> 6 files changed, 439 insertions(+) >> create mode 100644 arch/arm/include/asm/arch-tegra/tegra_nvec.h >> create mode 100644 drivers/i2c/tegra_nvec.c >> >> diff --git a/arch/arm/include/asm/arch-tegra/tegra_nvec.h >> b/arch/arm/include/asm/arch-tegra/tegra_nvec.h >> new file mode 100644 >> index 0000000..f1cec0d >> --- /dev/null >> +++ b/arch/arm/include/asm/arch-tegra/tegra_nvec.h >> @@ -0,0 +1,130 @@ >> +/* >> + * (C) Copyright 2014 >> + * Andrey Danin<danindrey@mail.ru> >> + * >> + * SPDX-License-Identifier: GPL-2.0+ >> + */ >> + >> +#ifndef _TEGRA_NVEC_H_ >> +#define _TEGRA_NVEC_H_ >> + >> +#define I2C_CNFG 0x00 >> +#define I2C_CNFG_PACKET_MODE_EN (1<<10) >> +#define I2C_CNFG_NEW_MASTER_SFM (1<<11) >> +#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12 >> + >> +#define I2C_SL_CNFG 0x20 >> +#define I2C_SL_NEWSL (1<<2) >> +#define I2C_SL_NACK (1<<1) >> +#define I2C_SL_RESP (1<<0) >> +#define I2C_SL_IRQ (1<<3) >> +#define END_TRANS (1<<4) >> +#define RCVD (1<<2) >> +#define RNW (1<<1) >> + >> +#define I2C_SL_RCVD 0x24 >> +#define I2C_SL_STATUS 0x28 >> +#define I2C_SL_ADDR1 0x2c >> +#define I2C_SL_ADDR2 0x30 >> +#define I2C_SL_DELAY_COUNT 0x3c >> + >> + > > please only one line, check globally. > I will. Thanks for pointing. > [...] >> diff --git a/drivers/i2c/tegra_nvec.c b/drivers/i2c/tegra_nvec.c >> new file mode 100644 >> index 0000000..b568988 >> --- /dev/null >> +++ b/drivers/i2c/tegra_nvec.c >> @@ -0,0 +1,294 @@ >> +/* >> + * (C) Copyright 2014 >> + * Andrey Danin<danindrey@mail.ru> >> + * >> + * SPDX-License-Identifier: GPL-2.0+ >> + */ >> + >> +#include<common.h> >> +#include<fdtdec.h> >> +#include<i2c.h> >> +#include<asm/io.h> >> +#include<asm/gpio.h> >> +#include<asm/arch/clock.h> >> +#include<asm/arch/funcmux.h> >> +#include<asm/arch-tegra/tegra_nvec.h> >> + >> +#ifndef CONFIG_SYS_I2C_TEGRA_NVEC >> +#error "You should enable CONFIG_SYS_I2C_TEGRA_NVEC" >> +#endif >> + >> +DECLARE_GLOBAL_DATA_PTR; >> + >> +struct nvec_periph devices[NVEC_LAST_MSG]; >> + >> + >> +struct nvec_t { >> + int i2c_bus; >> + struct fdt_gpio_state gpio; >> +} nvec_data; >> + >> + >> +/* nvec commands */ >> +char noop[] = { NVEC_CNTL, CNTL_NOOP }; >> + >> +void nvec_signal_request(void) >> +{ >> + gpio_set_value(nvec_data.gpio.gpio, 0); >> +} >> + >> + >> +void nvec_clear_request(void) >> +{ >> + gpio_set_value(nvec_data.gpio.gpio, 1); >> +} >> + >> + >> +int nvec_msg_is_event(const unsigned char *msg) >> +{ >> + return msg[0]>> 7; >> +} >> + >> + >> +int nvec_msg_event_type(const unsigned char *msg) >> +{ >> + return msg[0]& 0x0f; >> +} >> + >> + >> +/** >> + * Process incoming io message. >> + * If message is keyboard event then key code will >> + * be added to keys buffer. >> + * >> + * @param nvec nvec state struct >> + */ >> +void nvec_process_msg(struct nvec_t *nvec, struct i2c_transaction >> *trans) >> +{ >> + (void) nvec; >> + > > no empty line needed. > >> + const unsigned char *msg = (const unsigned char *)&trans->rx_buf[1]; >> + int event_type; >> + >> + if (!nvec_msg_is_event(msg)) >> + return; >> + >> + event_type = nvec_msg_event_type(msg); >> + >> + if (event_type< NVEC_KEYBOARD || event_type>= NVEC_LAST_MSG) >> + return; >> + >> + if (devices[event_type].process_msg) >> + devices[event_type].process_msg(msg); >> +} >> + >> + >> +/** >> + * Perform complete io operation (read or write). >> + * NOTE: function will wait NVEC_TIMEOUT_MIN (20ms) >> + * before status check to avoid nvec hang. >> + * >> + * @param nvec nvec state struct >> + * @param wait_for_ec if 1(NVEC_WAIT_FOR_EC) operation >> + * timeout is NVEC_TIMEOUT_MAX (600ms), >> + * otherwise function will return if io >> + * is not ready. >> + * >> + * @return nvec_io_* code >> + */ >> +int nvec_do_io(struct nvec_t *nvec) >> +{ >> + static unsigned int prev_timer; >> + unsigned int poll_start_ms = get_timer(prev_timer); >> + struct i2c_transaction trans; >> + int res; >> + >> + if (poll_start_ms> 30000) { >> + if (prev_timer != 0) >> + nvec_do_request(noop, sizeof(noop)); >> + prev_timer = get_timer(0); >> + } >> + >> + memset(&trans, 0, sizeof(trans)); >> + trans.start_timeout = 1; >> + trans.timeout = 6000; >> + >> + res = i2c_slave_io(&trans); >> + if (res == 0) { >> + nvec_process_msg(nvec,&trans); >> + return 0; >> + } >> + >> + if (res != -1) >> + debug("Error: i2c slave io failed with code %d\n", res); >> + >> + return -1; >> +} >> + >> +/** >> + * Send request and read response. If write or read failed >> + * operation will be repeated NVEC_ATTEMPTS_MAX times. >> + * >> + * @param buf request data >> + * @param size request data size >> + * @return 0 if ok, -1 on error >> + */ >> +int nvec_do_request(char *buf, int size) >> +{ >> + int res; >> + struct i2c_transaction trans; >> + int i; >> + >> + nvec_signal_request(); >> + >> + /* Request */ >> + for (i = 0; i< 10; ++i) { >> + memset(&trans, 0, sizeof(trans)); >> + trans.start_timeout = 600; >> + trans.timeout = 6000; >> + >> + trans.tx_buf[0] = (char)size; >> + memcpy(&trans.tx_buf[1], buf, size); >> + trans.tx_size = size + 1; >> + >> + res = i2c_slave_io(&trans); >> + if (res == 0) { >> + if (trans.tx_pos == trans.tx_size) >> + break; >> + >> + debug("Request was not sent completely"); >> + } else if (res != -1) { >> + debug("Unknown error while slave io"); >> + } >> + } >> + nvec_clear_request(); >> + if (res != 0) { >> + error("nvec failed to perform request\n"); >> + return -1; >> + } >> + >> + /* Response */ >> + for (i = 0; i< 10; ++i) { >> + memset(&trans, 0, sizeof(trans)); >> + trans.start_timeout = 600; >> + trans.timeout = 6000; >> + >> + res = i2c_slave_io(&trans); >> + if (res == 0) >> + break; >> + } >> + if (res != 0) { >> + error("nvec failed to read response\n"); >> + return -1; >> + } >> + >> + /* TODO Parse response */ > > ? > > Is this not a complete driver? > The driver is complete. This part is not mandatory because request and response are processed synchronously. I will write a proper comment here. >> + >> + return 0; >> +} >> + >> + >> +/** >> + * Decode the nvec information from the fdt. >> + * >> + * @param blob fdt blob >> + * @param nvec nvec device sturct >> + * @return 0 if ok, -ve on error > > What is "-ve" ? > It means -error_code. I will rework return codes, comments, style issues according to all your remarks. >> + */ >> +static int nvec_decode_config(const void *blob, >> + struct nvec_t *nvec) >> +{ >> + int node, parent; >> + int i2c_bus; >> + >> + node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_NVEC); >> + if (node< 0) { >> + error("Cannot find NVEC node in fdt\n"); >> + return node; >> + } >> + >> + parent = fdt_parent_offset(blob, node); >> + if (parent< 0) { >> + debug("%s: Cannot find node parent\n", __func__); >> + return -1; >> + } >> + >> + i2c_bus = i2c_get_bus_num_fdt(parent); >> + if (i2c_bus< 0) >> + return -1; >> + nvec->i2c_bus = i2c_bus; >> + >> + if (fdtdec_decode_gpio(blob, node, "request-gpios", >> + &nvec->gpio)) { >> + error("No NVEC request gpio\n"); >> + return -1; >> + } >> + >> + debug("NVEC: i2c:%d, gpio:%s(%u)\n", >> + nvec->i2c_bus, nvec->gpio.name, nvec->gpio.gpio); >> + return 0; >> +} >> + >> + >> +int board_nvec_init(void) >> +{ >> + int res = 0; >> + int i; >> + >> + if (nvec_decode_config(gd->fdt_blob,&nvec_data)) { >> + error("Can't parse NVEC node in device tree\n"); >> + return -1; >> + } >> + >> + debug("NVEC initialization...\n"); >> + >> + res = gpio_request(nvec_data.gpio.gpio, NULL); >> + if (res != 0) >> + error("NVEC: err, gpio_request\n"); >> + res = gpio_direction_output(nvec_data.gpio.gpio, 1); >> + if (res != 0) >> + error("NVEC: err, gpio_direction\n"); >> + res = gpio_set_value(nvec_data.gpio.gpio, 1); >> + if (res != 0) >> + error("NVEC: err, gpio_set_value\n"); >> + udelay(100); >> + >> + i2c_set_bus_num(nvec_data.i2c_bus); >> + >> + for (i = NVEC_KEYBOARD; i< NVEC_LAST_MSG; ++i) >> + if (devices[i].start) { >> + debug("Starting device %d(0x%x)\n", i, i); >> + devices[i].start(); >> + } >> + >> + return 1; > > Why "return 1"? > >> +} >> + >> + >> +int nvec_read_events(void) >> +{ >> + int res; >> + int cnt = 0; >> + >> + while (++cnt<= 8) { >> + res = nvec_do_io(&nvec_data); >> + if (res) >> + break; >> + >> + /* TODO Process nvec communication errors */ > > Again a "TODO" ? > I will implement. >> + } >> + >> + return cnt; >> +} >> + >> + >> +int nvec_register_periph(int msg_type, struct nvec_periph *periph) >> +{ >> + if (devices[msg_type].start || devices[msg_type].process_msg) { >> + error("Device for msg %d already registered\n", msg_type); >> + return -1; >> + } >> + >> + devices[msg_type] = *periph; >> + return 0; >> +} >> diff --git a/include/fdtdec.h b/include/fdtdec.h >> index 19bab79..7b84335 100644 >> --- a/include/fdtdec.h >> +++ b/include/fdtdec.h >> @@ -64,6 +64,7 @@ enum fdt_compat_id { >> COMPAT_NVIDIA_TEGRA20_SDMMC, /* Tegra20 SDMMC controller */ >> COMPAT_NVIDIA_TEGRA20_SFLASH, /* Tegra 2 SPI flash controller */ >> COMPAT_NVIDIA_TEGRA20_SLINK, /* Tegra 2 SPI SLINK controller */ >> + COMPAT_NVIDIA_TEGRA20_NVEC, /* Tegra 2 EC controller */ >> COMPAT_NVIDIA_TEGRA114_SPI, /* Tegra 114 SPI controller */ >> COMPAT_SMSC_LAN9215, /* SMSC 10/100 Ethernet LAN9215 */ >> COMPAT_SAMSUNG_EXYNOS5_SROMC, /* Exynos5 SROMC */ >> diff --git a/lib/fdtdec.c b/lib/fdtdec.c >> index 1fecab3..82df701 100644 >> --- a/lib/fdtdec.c >> +++ b/lib/fdtdec.c >> @@ -37,6 +37,7 @@ static const char * const compat_names[COMPAT_COUNT] >> = { >> COMPAT(NVIDIA_TEGRA20_SDMMC, "nvidia,tegra20-sdhci"), >> COMPAT(NVIDIA_TEGRA20_SFLASH, "nvidia,tegra20-sflash"), >> COMPAT(NVIDIA_TEGRA20_SLINK, "nvidia,tegra20-slink"), >> + COMPAT(NVIDIA_TEGRA20_NVEC, "nvidia,tegra20-nvec"), >> COMPAT(NVIDIA_TEGRA114_SPI, "nvidia,tegra114-spi"), >> COMPAT(SMSC_LAN9215, "smsc,lan9215"), >> COMPAT(SAMSUNG_EXYNOS5_SROMC, "samsung,exynos-sromc"), > > bye, > Heiko Thanks.
Hello Andrey, Am 29.04.2014 09:15, schrieb Andrey Danin: > On 29.04.2014 9:43, Heiko Schocher wrote: >> Hello Andrey, >> > Hello Heiko. > > First of all, thank you for the review! You are welcome. >> Am 27.04.2014 03:14, schrieb Andrey Danin: >>> Signed-off-by: Andrey Danin<danindrey@mail.ru> >>> CC: Stephen Warren<swarren@nvidia.com> >>> CC: Marc Dietrich<marvin24@gmx.de> >>> CC: Julian Andres Klode<jak@jak-linux.org> >>> CC: ac100@lists.launchpad.net >>> --- >>> Changes for v2: >>> - NVEC driver was reworked to use tegra-i2c >>> >>> arch/arm/include/asm/arch-tegra/tegra_nvec.h | 130 ++++++++++++ >>> board/nvidia/common/board.c | 12 ++ >>> drivers/i2c/Makefile | 1 + >>> drivers/i2c/tegra_nvec.c | 294 >>> ++++++++++++++++++++++++++ >>> include/fdtdec.h | 1 + >>> lib/fdtdec.c | 1 + >>> 6 files changed, 439 insertions(+) >>> create mode 100644 arch/arm/include/asm/arch-tegra/tegra_nvec.h >>> create mode 100644 drivers/i2c/tegra_nvec.c [...] >>> diff --git a/drivers/i2c/tegra_nvec.c b/drivers/i2c/tegra_nvec.c >>> new file mode 100644 >>> index 0000000..b568988 >>> --- /dev/null >>> +++ b/drivers/i2c/tegra_nvec.c >>> @@ -0,0 +1,294 @@ [...] >>> + /* TODO Parse response */ >> >> ? >> >> Is this not a complete driver? >> > The driver is complete. This part is not mandatory because request and response are processed synchronously. I will write a proper comment here. Ok, thanks! >>> + >>> + return 0; >>> +} >>> + >>> + >>> +/** >>> + * Decode the nvec information from the fdt. >>> + * >>> + * @param blob fdt blob >>> + * @param nvec nvec device sturct >>> + * @return 0 if ok, -ve on error >> >> What is "-ve" ? >> > It means -error_code. > I will rework return codes, comments, style issues according to all your remarks. Thanks! [...] >>> +int nvec_read_events(void) >>> +{ >>> + int res; >>> + int cnt = 0; >>> + >>> + while (++cnt<= 8) { >>> + res = nvec_do_io(&nvec_data); >>> + if (res) >>> + break; >>> + >>> + /* TODO Process nvec communication errors */ >> >> Again a "TODO" ? >> > I will implement. Sounds great, thanks! [...] bye, Heiko
diff --git a/arch/arm/include/asm/arch-tegra/tegra_nvec.h b/arch/arm/include/asm/arch-tegra/tegra_nvec.h new file mode 100644 index 0000000..f1cec0d --- /dev/null +++ b/arch/arm/include/asm/arch-tegra/tegra_nvec.h @@ -0,0 +1,130 @@ +/* + * (C) Copyright 2014 + * Andrey Danin <danindrey@mail.ru> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _TEGRA_NVEC_H_ +#define _TEGRA_NVEC_H_ + +#define I2C_CNFG 0x00 +#define I2C_CNFG_PACKET_MODE_EN (1<<10) +#define I2C_CNFG_NEW_MASTER_SFM (1<<11) +#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12 + +#define I2C_SL_CNFG 0x20 +#define I2C_SL_NEWSL (1<<2) +#define I2C_SL_NACK (1<<1) +#define I2C_SL_RESP (1<<0) +#define I2C_SL_IRQ (1<<3) +#define END_TRANS (1<<4) +#define RCVD (1<<2) +#define RNW (1<<1) + +#define I2C_SL_RCVD 0x24 +#define I2C_SL_STATUS 0x28 +#define I2C_SL_ADDR1 0x2c +#define I2C_SL_ADDR2 0x30 +#define I2C_SL_DELAY_COUNT 0x3c + + +enum nvec_msg_type { + NVEC_KEYBOARD = 0, + NVEC_SYS = 1, + NVEC_BAT, + NVEC_GPIO, + NVEC_SLEEP, + NVEC_KBD, + NVEC_PS2, + NVEC_CNTL, + NVEC_OEM0 = 0x0d, + NVEC_KB_EVT = 0x80, + NVEC_PS2_EVT, + NVEC_LAST_MSG, +}; + +enum nvec_event_size { + NVEC_2BYTES, + NVEC_3BYTES, + NVEC_VAR_SIZE, +}; + +enum sys_subcmds { + SYS_GET_STATUS, + SYS_CNFG_EVENT_REPORTING, + SYS_ACK_STATUS, + SYS_CNFG_WAKE = 0xfd, +}; + +enum kbd_subcmds { + CNFG_WAKE = 3, + CNFG_WAKE_KEY_REPORTING, + SET_LEDS = 0xed, + ENABLE_KBD = 0xf4, + DISABLE_KBD, +}; + +enum cntl_subcmds { + CNTL_RESET_EC = 0x00, + CNTL_SELF_TEST = 0x01, + CNTL_NOOP = 0x02, + CNTL_GET_EC_SPEC_VER = 0x10, + CNTL_GET_FIRMWARE_VERSION = 0x15, +}; + +enum nvec_sleep_subcmds { + GLOBAL_EVENTS, + AP_PWR_DOWN, + AP_SUSPEND, +}; + +enum ps2_subcmds { + MOUSE_SEND_CMD = 0x01 +}; + +#define MOUSE_RESET 0xff + +typedef int (*periph_start)(void); +typedef int (*periph_process_msg)(const unsigned char *); + +struct nvec_periph { + periph_start start; + periph_process_msg process_msg; +}; + + +int board_nvec_init(void); + +/** + * Read all available events. + * + * @return count of available events if ok, -1 on error + */ +int nvec_read_events(void); + +int nvec_msg_is_event(const unsigned char *msg); +int nvec_msg_event_type(const unsigned char *msg); + +/** + * Register perepherial device driver. + * + * @param msg_type type of messages that divece processes. + * @param periph pointer to device description. + * + * @return 0 if ok, -1 on error + */ +int nvec_register_periph(int msg_type, struct nvec_periph *periph); + +/** + * Send request and read response. If write or read failed + * operation will be repeated NVEC_ATTEMPTS_MAX times. + * + * @param buf request data + * @param size request data size + * @return 0 if ok, -1 on error + */ +int nvec_do_request(char *buf, int size); + + +#endif /* _TEGRA_NVEC_H_ */ diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c index 3b18e28..6d3055c 100644 --- a/board/nvidia/common/board.c +++ b/board/nvidia/common/board.c @@ -38,6 +38,9 @@ #include <asm/arch-tegra/tegra_mmc.h> #include <asm/arch-tegra/mmc.h> #endif +#ifdef CONFIG_SYS_I2C_TEGRA_NVEC +#include <asm/arch-tegra/tegra_nvec.h> +#endif #include <i2c.h> #include <spi.h> #include "emc.h" @@ -194,10 +197,19 @@ int board_early_init_f(void) int board_late_init(void) { + __maybe_unused int err; + #ifdef CONFIG_LCD /* Make sure we finish initing the LCD */ tegra_lcd_check_next_stage(gd->fdt_blob, 1); #endif + +#ifdef CONFIG_SYS_I2C_TEGRA_NVEC + err = board_nvec_init(); + if (err) + debug("NVEC controller init failed: %d\n", err); +#endif + return 0; } diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index fa3a875..3041191 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -27,4 +27,5 @@ obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o +obj-$(CONFIG_SYS_I2C_TEGRA_NVEC) += tegra_nvec.o obj-$(CONFIG_SYS_I2C_ZYNQ) += zynq_i2c.o diff --git a/drivers/i2c/tegra_nvec.c b/drivers/i2c/tegra_nvec.c new file mode 100644 index 0000000..b568988 --- /dev/null +++ b/drivers/i2c/tegra_nvec.c @@ -0,0 +1,294 @@ +/* + * (C) Copyright 2014 + * Andrey Danin <danindrey@mail.ru> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <i2c.h> +#include <asm/io.h> +#include <asm/gpio.h> +#include <asm/arch/clock.h> +#include <asm/arch/funcmux.h> +#include <asm/arch-tegra/tegra_nvec.h> + +#ifndef CONFIG_SYS_I2C_TEGRA_NVEC +#error "You should enable CONFIG_SYS_I2C_TEGRA_NVEC" +#endif + +DECLARE_GLOBAL_DATA_PTR; + +struct nvec_periph devices[NVEC_LAST_MSG]; + + +struct nvec_t { + int i2c_bus; + struct fdt_gpio_state gpio; +} nvec_data; + + +/* nvec commands */ +char noop[] = { NVEC_CNTL, CNTL_NOOP }; + +void nvec_signal_request(void) +{ + gpio_set_value(nvec_data.gpio.gpio, 0); +} + + +void nvec_clear_request(void) +{ + gpio_set_value(nvec_data.gpio.gpio, 1); +} + + +int nvec_msg_is_event(const unsigned char *msg) +{ + return msg[0] >> 7; +} + + +int nvec_msg_event_type(const unsigned char *msg) +{ + return msg[0] & 0x0f; +} + + +/** + * Process incoming io message. + * If message is keyboard event then key code will + * be added to keys buffer. + * + * @param nvec nvec state struct + */ +void nvec_process_msg(struct nvec_t *nvec, struct i2c_transaction *trans) +{ + (void) nvec; + + const unsigned char *msg = (const unsigned char *)&trans->rx_buf[1]; + int event_type; + + if (!nvec_msg_is_event(msg)) + return; + + event_type = nvec_msg_event_type(msg); + + if (event_type < NVEC_KEYBOARD || event_type >= NVEC_LAST_MSG) + return; + + if (devices[event_type].process_msg) + devices[event_type].process_msg(msg); +} + + +/** + * Perform complete io operation (read or write). + * NOTE: function will wait NVEC_TIMEOUT_MIN (20ms) + * before status check to avoid nvec hang. + * + * @param nvec nvec state struct + * @param wait_for_ec if 1(NVEC_WAIT_FOR_EC) operation + * timeout is NVEC_TIMEOUT_MAX (600ms), + * otherwise function will return if io + * is not ready. + * + * @return nvec_io_* code + */ +int nvec_do_io(struct nvec_t *nvec) +{ + static unsigned int prev_timer; + unsigned int poll_start_ms = get_timer(prev_timer); + struct i2c_transaction trans; + int res; + + if (poll_start_ms > 30000) { + if (prev_timer != 0) + nvec_do_request(noop, sizeof(noop)); + prev_timer = get_timer(0); + } + + memset(&trans, 0, sizeof(trans)); + trans.start_timeout = 1; + trans.timeout = 6000; + + res = i2c_slave_io(&trans); + if (res == 0) { + nvec_process_msg(nvec, &trans); + return 0; + } + + if (res != -1) + debug("Error: i2c slave io failed with code %d\n", res); + + return -1; +} + +/** + * Send request and read response. If write or read failed + * operation will be repeated NVEC_ATTEMPTS_MAX times. + * + * @param buf request data + * @param size request data size + * @return 0 if ok, -1 on error + */ +int nvec_do_request(char *buf, int size) +{ + int res; + struct i2c_transaction trans; + int i; + + nvec_signal_request(); + + /* Request */ + for (i = 0; i < 10; ++i) { + memset(&trans, 0, sizeof(trans)); + trans.start_timeout = 600; + trans.timeout = 6000; + + trans.tx_buf[0] = (char)size; + memcpy(&trans.tx_buf[1], buf, size); + trans.tx_size = size + 1; + + res = i2c_slave_io(&trans); + if (res == 0) { + if (trans.tx_pos == trans.tx_size) + break; + + debug("Request was not sent completely"); + } else if (res != -1) { + debug("Unknown error while slave io"); + } + } + nvec_clear_request(); + if (res != 0) { + error("nvec failed to perform request\n"); + return -1; + } + + /* Response */ + for (i = 0; i < 10; ++i) { + memset(&trans, 0, sizeof(trans)); + trans.start_timeout = 600; + trans.timeout = 6000; + + res = i2c_slave_io(&trans); + if (res == 0) + break; + } + if (res != 0) { + error("nvec failed to read response\n"); + return -1; + } + + /* TODO Parse response */ + + return 0; +} + + +/** + * Decode the nvec information from the fdt. + * + * @param blob fdt blob + * @param nvec nvec device sturct + * @return 0 if ok, -ve on error + */ +static int nvec_decode_config(const void *blob, + struct nvec_t *nvec) +{ + int node, parent; + int i2c_bus; + + node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_NVEC); + if (node < 0) { + error("Cannot find NVEC node in fdt\n"); + return node; + } + + parent = fdt_parent_offset(blob, node); + if (parent < 0) { + debug("%s: Cannot find node parent\n", __func__); + return -1; + } + + i2c_bus = i2c_get_bus_num_fdt(parent); + if (i2c_bus < 0) + return -1; + nvec->i2c_bus = i2c_bus; + + if (fdtdec_decode_gpio(blob, node, "request-gpios", + &nvec->gpio)) { + error("No NVEC request gpio\n"); + return -1; + } + + debug("NVEC: i2c:%d, gpio:%s(%u)\n", + nvec->i2c_bus, nvec->gpio.name, nvec->gpio.gpio); + return 0; +} + + +int board_nvec_init(void) +{ + int res = 0; + int i; + + if (nvec_decode_config(gd->fdt_blob, &nvec_data)) { + error("Can't parse NVEC node in device tree\n"); + return -1; + } + + debug("NVEC initialization...\n"); + + res = gpio_request(nvec_data.gpio.gpio, NULL); + if (res != 0) + error("NVEC: err, gpio_request\n"); + res = gpio_direction_output(nvec_data.gpio.gpio, 1); + if (res != 0) + error("NVEC: err, gpio_direction\n"); + res = gpio_set_value(nvec_data.gpio.gpio, 1); + if (res != 0) + error("NVEC: err, gpio_set_value\n"); + udelay(100); + + i2c_set_bus_num(nvec_data.i2c_bus); + + for (i = NVEC_KEYBOARD; i < NVEC_LAST_MSG; ++i) + if (devices[i].start) { + debug("Starting device %d(0x%x)\n", i, i); + devices[i].start(); + } + + return 1; +} + + +int nvec_read_events(void) +{ + int res; + int cnt = 0; + + while (++cnt <= 8) { + res = nvec_do_io(&nvec_data); + if (res) + break; + + /* TODO Process nvec communication errors */ + } + + return cnt; +} + + +int nvec_register_periph(int msg_type, struct nvec_periph *periph) +{ + if (devices[msg_type].start || devices[msg_type].process_msg) { + error("Device for msg %d already registered\n", msg_type); + return -1; + } + + devices[msg_type] = *periph; + return 0; +} diff --git a/include/fdtdec.h b/include/fdtdec.h index 19bab79..7b84335 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -64,6 +64,7 @@ enum fdt_compat_id { COMPAT_NVIDIA_TEGRA20_SDMMC, /* Tegra20 SDMMC controller */ COMPAT_NVIDIA_TEGRA20_SFLASH, /* Tegra 2 SPI flash controller */ COMPAT_NVIDIA_TEGRA20_SLINK, /* Tegra 2 SPI SLINK controller */ + COMPAT_NVIDIA_TEGRA20_NVEC, /* Tegra 2 EC controller */ COMPAT_NVIDIA_TEGRA114_SPI, /* Tegra 114 SPI controller */ COMPAT_SMSC_LAN9215, /* SMSC 10/100 Ethernet LAN9215 */ COMPAT_SAMSUNG_EXYNOS5_SROMC, /* Exynos5 SROMC */ diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 1fecab3..82df701 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -37,6 +37,7 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(NVIDIA_TEGRA20_SDMMC, "nvidia,tegra20-sdhci"), COMPAT(NVIDIA_TEGRA20_SFLASH, "nvidia,tegra20-sflash"), COMPAT(NVIDIA_TEGRA20_SLINK, "nvidia,tegra20-slink"), + COMPAT(NVIDIA_TEGRA20_NVEC, "nvidia,tegra20-nvec"), COMPAT(NVIDIA_TEGRA114_SPI, "nvidia,tegra114-spi"), COMPAT(SMSC_LAN9215, "smsc,lan9215"), COMPAT(SAMSUNG_EXYNOS5_SROMC, "samsung,exynos-sromc"),
Signed-off-by: Andrey Danin <danindrey@mail.ru> CC: Stephen Warren <swarren@nvidia.com> CC: Marc Dietrich <marvin24@gmx.de> CC: Julian Andres Klode <jak@jak-linux.org> CC: ac100@lists.launchpad.net --- Changes for v2: - NVEC driver was reworked to use tegra-i2c arch/arm/include/asm/arch-tegra/tegra_nvec.h | 130 ++++++++++++ board/nvidia/common/board.c | 12 ++ drivers/i2c/Makefile | 1 + drivers/i2c/tegra_nvec.c | 294 ++++++++++++++++++++++++++ include/fdtdec.h | 1 + lib/fdtdec.c | 1 + 6 files changed, 439 insertions(+) create mode 100644 arch/arm/include/asm/arch-tegra/tegra_nvec.h create mode 100644 drivers/i2c/tegra_nvec.c