From 547b8de07745392dbfc47645769fa6cf03ed4a38 Mon Sep 17 00:00:00 2001 From: YH Lin Date: Fri, 21 Apr 2017 14:46:23 -0700 Subject: strago: fix builder errors The builders for firmware-cyan-7287.57.B branch (pre-flight and strago) have failed due to the removal of board/strago directory (CL:359322). Therefore restoring this directory and adding gyro_l3gd20h files from firmware-strago-7287.B:5399a6245d2dc21c2234f0d908032ab7fa6ac6e7 to get the builders working again. BRANCH=firmware-cyan-7287.57.B BUG=None TEST=./cbuildbot --local strago-pre-flight-branch TEST=./cbuildbot --local strago-firmware Change-Id: I495a10e4150775fbb2942a73a1eabb19d47ae968 Signed-off-by: YH Lin Reviewed-on: https://chromium-review.googlesource.com/484744 --- board/strago/Makefile | 1 + board/strago/battery.c | 43 +++++ board/strago/board.c | 227 ++++++++++++++++++++++++ board/strago/board.h | 165 +++++++++++++++++ board/strago/build.mk | 14 ++ board/strago/ec.tasklist | 29 +++ board/strago/gpio.inc | 150 ++++++++++++++++ board/strago/led.c | 210 ++++++++++++++++++++++ board/strago/lfw/gpio.inc | 16 ++ driver/gyro_l3gd20h.c | 439 ++++++++++++++++++++++++++++++++++++++++++++++ driver/gyro_l3gd20h.h | 102 +++++++++++ 11 files changed, 1396 insertions(+) create mode 120000 board/strago/Makefile create mode 100644 board/strago/battery.c create mode 100644 board/strago/board.c create mode 100644 board/strago/board.h create mode 100644 board/strago/build.mk create mode 100644 board/strago/ec.tasklist create mode 100644 board/strago/gpio.inc create mode 100644 board/strago/led.c create mode 100644 board/strago/lfw/gpio.inc create mode 100644 driver/gyro_l3gd20h.c create mode 100644 driver/gyro_l3gd20h.h diff --git a/board/strago/Makefile b/board/strago/Makefile new file mode 120000 index 0000000000..94aaae2c4d --- /dev/null +++ b/board/strago/Makefile @@ -0,0 +1 @@ +../../Makefile \ No newline at end of file diff --git a/board/strago/battery.c b/board/strago/battery.c new file mode 100644 index 0000000000..43fa956ced --- /dev/null +++ b/board/strago/battery.c @@ -0,0 +1,43 @@ +/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Battery pack vendor provided charging profile + */ + +#include "battery.h" +#include "battery_smart.h" + +/* Shutdown mode parameter to write to manufacturer access register */ +#define SB_SHUTDOWN_DATA 0x0010 + +static const struct battery_info info = { + .voltage_max = 8700,/* mV */ + .voltage_normal = 7600, + .voltage_min = 6000, + .precharge_current = 150,/* mA */ + .start_charging_min_c = 0, + .start_charging_max_c = 45, + .charging_min_c = 0, + .charging_max_c = 45, + .discharging_min_c = -20, + .discharging_max_c = 60, +}; + +const struct battery_info *battery_get_info(void) +{ + return &info; +} + +int board_cut_off_battery(void) +{ + int rv; + + /* Ship mode command must be sent twice to take effect */ + rv = sb_write(SB_MANUFACTURER_ACCESS, SB_SHUTDOWN_DATA); + + if (rv != EC_SUCCESS) + return rv; + + return sb_write(SB_MANUFACTURER_ACCESS, SB_SHUTDOWN_DATA); +} diff --git a/board/strago/board.c b/board/strago/board.c new file mode 100644 index 0000000000..c749d809ac --- /dev/null +++ b/board/strago/board.c @@ -0,0 +1,227 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +/* Strago board-specific configuration */ + +#include "adc.h" +#include "als.h" +#include "button.h" +#include "charger.h" +#include "charge_state.h" +#include "driver/accel_kionix.h" +#include "driver/als_isl29035.h" +#include "driver/gyro_l3gd20h.h" +#include "driver/temp_sensor/tmp432.h" +#include "extpower.h" +#include "gpio.h" +#include "hooks.h" +#include "host_command.h" +#include "i2c.h" +#include "lid_switch.h" +#include "math_util.h" +#include "motion_lid.h" +#include "motion_sense.h" +#include "power.h" +#include "power_button.h" +#include "pwm.h" +#include "pwm_chip.h" +#include "registers.h" +#include "temp_sensor.h" +#include "temp_sensor_chip.h" +#include "thermal.h" +#include "uart.h" +#include "util.h" + +#define GPIO_KB_INPUT (GPIO_INPUT | GPIO_PULL_UP) +#define GPIO_KB_OUTPUT (GPIO_ODR_HIGH) +#define GPIO_KB_OUTPUT_COL2 (GPIO_OUT_LOW) + +#include "gpio_list.h" + +/* PWM channels. Must be in the exactly same order as in enum pwm_channel. */ +const struct pwm_t pwm_channels[] = { + {0, PWM_CONFIG_ACTIVE_LOW}, + {1, PWM_CONFIG_ACTIVE_LOW}, + {3, PWM_CONFIG_ACTIVE_LOW}, +}; + +BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT); + +/* power signal list. Must match order of enum power_signal. */ +const struct power_signal_info power_signal_list[] = { + {GPIO_ALL_SYS_PGOOD, 1, "ALL_SYS_PWRGD"}, + {GPIO_RSMRST_L_PGOOD, 1, "RSMRST_N_PWRGD"}, + {GPIO_PCH_SLP_S3_L, 1, "SLP_S3#_DEASSERTED"}, + {GPIO_PCH_SLP_S4_L, 1, "SLP_S4#_DEASSERTED"}, +}; +BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); + +const struct i2c_port_t i2c_ports[] = { + {"batt_chg", MEC1322_I2C0_0, 100, + GPIO_I2C_PORT0_0_SCL, GPIO_I2C_PORT0_0_SDA}, + {"muxes", MEC1322_I2C0_1, 100, + GPIO_I2C_PORT0_1_SCL, GPIO_I2C_PORT0_1_SDA}, + {"pd_mcu", MEC1322_I2C1, 1000, + GPIO_I2C_PORT1_SCL, GPIO_I2C_PORT1_SDA}, + {"sensors", MEC1322_I2C2, 100, + GPIO_I2C_PORT2_SCL, GPIO_I2C_PORT2_SDA}, + {"thermal", MEC1322_I2C3, 100, + GPIO_I2C_PORT3_SCL, GPIO_I2C_PORT3_SDA} +}; +const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports); + +const enum gpio_signal hibernate_wake_pins[] = { + GPIO_POWER_BUTTON_L, +}; + +const int hibernate_wake_pins_used = ARRAY_SIZE(hibernate_wake_pins); + +/* + * Temperature sensors data; must be in same order as enum temp_sensor_id. + * Sensor index and name must match those present in coreboot: + * src/mainboard/google/${board}/acpi/dptf.asl + */ +const struct temp_sensor_t temp_sensors[] = { + {"TMP432_Internal", TEMP_SENSOR_TYPE_BOARD, tmp432_get_val, + TMP432_IDX_LOCAL, 4}, + {"TMP432_Sensor_1", TEMP_SENSOR_TYPE_BOARD, tmp432_get_val, + TMP432_IDX_REMOTE1, 4}, + {"TMP432_Sensor_2", TEMP_SENSOR_TYPE_BOARD, tmp432_get_val, + TMP432_IDX_REMOTE2, 4}, + {"Battery", TEMP_SENSOR_TYPE_BATTERY, charge_temp_sensor_get_val, + 0, 4}, +}; +BUILD_ASSERT(ARRAY_SIZE(temp_sensors) == TEMP_SENSOR_COUNT); + +/* ALS instances. Must be in same order as enum als_id. */ +struct als_t als[] = { + {"ISL", isl29035_read_lux, 5}, +}; +BUILD_ASSERT(ARRAY_SIZE(als) == ALS_COUNT); + +/* Thermal limits for each temp sensor. All temps are in degrees K. Must be in + * same order as enum temp_sensor_id. To always ignore any temp, use 0. + */ +struct ec_thermal_config thermal_params[] = { + {{0, 0, 0}, 0, 0}, /* TMP432_Internal */ + {{0, 0, 0}, 0, 0}, /* TMP432_Sensor_1 */ + {{0, 0, 0}, 0, 0}, /* TMP432_Sensor_2 */ + {{0, 0, 0}, 0, 0}, /* Battery Sensor */ +}; +BUILD_ASSERT(ARRAY_SIZE(thermal_params) == TEMP_SENSOR_COUNT); + +const struct button_config buttons[] = { + {"Volume Down", KEYBOARD_BUTTON_VOLUME_DOWN, GPIO_VOLUME_DOWN, + 30 * MSEC, 0}, + {"Volume Up", KEYBOARD_BUTTON_VOLUME_UP, GPIO_VOLUME_UP, + 30 * MSEC, 0}, +}; +BUILD_ASSERT(ARRAY_SIZE(buttons) == CONFIG_BUTTON_COUNT); + +/* Four Motion sensors */ +/* kxcj9 mutex and local/private data*/ +static struct mutex g_kxcj9_mutex[2]; +struct kionix_accel_data g_kxcj9_data[2]; + +/* Matrix to rotate accelrator into standard reference frame */ +const matrix_3x3_t base_standard_ref = { + { 0, FLOAT_TO_FP(1), 0}, + {FLOAT_TO_FP(-1), 0, 0}, + { 0, 0, FLOAT_TO_FP(1)} +}; + +const matrix_3x3_t lid_standard_ref = { + {FLOAT_TO_FP(1), 0, 0}, + { 0, FLOAT_TO_FP(1), 0}, + { 0, 0, FLOAT_TO_FP(1)} +}; + +struct motion_sensor_t motion_sensors[] = { + [BASE_ACCEL] = { + .name = "Base Accel", + .active_mask = SENSOR_ACTIVE_S0_S3, + .chip = MOTIONSENSE_CHIP_KXCJ9, + .type = MOTIONSENSE_TYPE_ACCEL, + .location = MOTIONSENSE_LOC_BASE, + .drv = &kionix_accel_drv, + .mutex = &g_kxcj9_mutex[0], + .drv_data = &g_kxcj9_data[0], + .port = I2C_PORT_ACCEL, + .addr = KXCJ9_ADDR1, + .rot_standard_ref = &base_standard_ref, + .default_range = 2, /* g, enough for laptop. */ + .config = { + /* AP: by default shutdown all sensors */ + [SENSOR_CONFIG_AP] = { + .odr = 0, + .ec_rate = 0, + }, + /* EC use accel for angle detection */ + [SENSOR_CONFIG_EC_S0] = { + .odr = 10000 | ROUND_UP_FLAG, + .ec_rate = 0, + }, + /* Sensor off in S3/S5 */ + [SENSOR_CONFIG_EC_S3] = { + .odr = 0, + .ec_rate = 0 + }, + /* Sensor off in S3/S5 */ + [SENSOR_CONFIG_EC_S5] = { + .odr = 0, + .ec_rate = 0 + }, + } + }, + [LID_ACCEL] = { + .name = "Lid Accel", + .active_mask = SENSOR_ACTIVE_S0_S3, + .chip = MOTIONSENSE_CHIP_KXCJ9, + .type = MOTIONSENSE_TYPE_ACCEL, + .location = MOTIONSENSE_LOC_LID, + .drv = &kionix_accel_drv, + .mutex = &g_kxcj9_mutex[1], + .drv_data = &g_kxcj9_data[1], + .port = I2C_PORT_ACCEL, + .addr = KXCJ9_ADDR0, + .rot_standard_ref = &lid_standard_ref, + .default_range = 2, /* g, enough for laptop. */ + .config = { + /* AP: by default shutdown all sensors */ + [SENSOR_CONFIG_AP] = { + .odr = 0, + .ec_rate = 0, + }, + /* EC use accel for angle detection */ + [SENSOR_CONFIG_EC_S0] = { + .odr = 10000 | ROUND_UP_FLAG, + .ec_rate = 0, + }, + /* Sensor off in S3/S5 */ + [SENSOR_CONFIG_EC_S3] = { + .odr = 0, + .ec_rate = 0 + }, + /* Sensor off in S3/S5 */ + [SENSOR_CONFIG_EC_S5] = { + .odr = 0, + .ec_rate = 0 + }, + }, + }, +}; +const unsigned int motion_sensor_count = ARRAY_SIZE(motion_sensors); + +/* init ADC ports to avoid floating state due to thermistors */ +static void adc_pre_init(void) +{ + /* Configure GPIOs */ + gpio_config_module(MODULE_ADC, 1); +} +DECLARE_HOOK(HOOK_INIT, adc_pre_init, HOOK_PRIO_INIT_ADC - 1); + +int i2c_port_is_smbus(int port) +{ + return (port == MEC1322_I2C0_0 || port == MEC1322_I2C0_1) ? 1 : 0; +} diff --git a/board/strago/board.h b/board/strago/board.h new file mode 100644 index 0000000000..29214bec14 --- /dev/null +++ b/board/strago/board.h @@ -0,0 +1,165 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Strago board configuration */ + +#ifndef __CROS_EC_BOARD_H +#define __CROS_EC_BOARD_H + +/* Optional features */ +#define CONFIG_WATCHDOG_HELP +#define CONFIG_CLOCK_CRYSTAL +#define CONFIG_CHIPSET_BRASWELL +#define CONFIG_SCI_GPIO GPIO_PCH_SCI_L + +#define CONFIG_BOARD_VERSION + +#define CONFIG_KEYBOARD_COL2_INVERTED +#define CONFIG_KEYBOARD_IRQ_GPIO GPIO_KBD_IRQ_L +#undef CONFIG_KEYBOARD_KSO_BASE +#define CONFIG_KEYBOARD_KSO_BASE 4 /* KSO starts from KSO04 */ +#define CONFIG_KEYBOARD_PROTOCOL_8042 +#define CONFIG_POWER_BUTTON +#define CONFIG_POWER_BUTTON_X86 +#define CONFIG_LID_SWITCH +#define CONFIG_LOW_POWER_IDLE +#define CONFIG_LOW_POWER_PSEUDO_G3 +#define CONFIG_POWER_COMMON +#define CONFIG_POWER_SHUTDOWN_PAUSE_IN_S5 +#define CONFIG_EXTPOWER_GPIO +#define CONFIG_VBOOT_HASH +#define CONFIG_PORT80_TASK_EN +#define CONFIG_WIRELESS + +#define CONFIG_SPI_PORT 1 +#define CONFIG_SPI_CS_GPIO GPIO_PVT_CS0 +#define CONFIG_SPI_FLASH +#define CONFIG_SPI_FLASH_SIZE 524288 +#define CONFIG_SPI_FLASH_W25Q64 + +#define CONFIG_USB_PORT_POWER_SMART +#define CONFIG_USB_PORT_POWER_SMART_SIMPLE + +#define CONFIG_TEMP_SENSOR +#define CONFIG_TEMP_SENSOR_TMP432 + +#define CONFIG_PMIC + +#define CONFIG_ALS +#define CONFIG_ALS_ISL29035 +#define CONFIG_BATTERY_CUT_OFF +#define CONFIG_BATTERY_PRESENT_GPIO GPIO_BAT_PRESENT_L +#define CONFIG_BATTERY_SMART +#define CONFIG_CHARGER +#define CONFIG_CHARGER_V2 +#define CONFIG_CHARGER_BQ24770 +#define CONFIG_CHARGER_ILIM_PIN_DISABLED +#define CONFIG_CHARGER_SENSE_RESISTOR 10 +#define CONFIG_CHARGER_SENSE_RESISTOR_AC 10 +#define CONFIG_CHARGER_INPUT_CURRENT 2240 +#define CONFIG_CHARGER_DISCHARGE_ON_AC + +#define CONFIG_PWM +#define CONFIG_LED_COMMON + +#define CONFIG_I2C + +/* Accelerometer */ +#define CONFIG_ACCEL_KXCJ9 +#define CONFIG_CMD_ACCELS +#define CONFIG_CMD_ACCEL_INFO +#define CONFIG_LID_ANGLE + +/* Wireless signals */ +#define WIRELESS_GPIO_WLAN GPIO_WLAN_OFF_L + +/* Number of buttons */ +#define CONFIG_BUTTON_COUNT 2 + +/* Modules we want to exclude */ +#undef CONFIG_EEPROM +#undef CONFIG_EOPTION +#undef CONFIG_PSTORE +#undef CONFIG_PECI +#undef CONFIG_FANS +#undef CONFIG_ADC +#ifndef __ASSEMBLER__ + +#include "gpio_signal.h" +#include "registers.h" + +/* I2C ports */ +#define I2C_PORT_BATTERY MEC1322_I2C0_0 +#define I2C_PORT_CHARGER MEC1322_I2C0_0 +#define I2C_PORT_PD_MCU MEC1322_I2C1 +#define I2C_PORT_TCPC MEC1322_I2C1 +#define I2C_PORT_ACCEL MEC1322_I2C2 +#define I2C_PORT_GYRO MEC1322_I2C2 +#define I2C_PORT_ALS MEC1322_I2C2 +#define I2C_PORT_THERMAL MEC1322_I2C3 + +/* ADC signal */ +enum adc_channel { + /* Number of ADC channels */ + ADC_CH_COUNT +}; + +/* Sensor index definition */ +enum sensor_id { + BASE_ACCEL = 0, + LID_ACCEL = 1, +}; + +/* + * We have not enabled the sensor FIFO on the accels, so we force the EC + * to collect at every sample. + */ +#define CONFIG_ACCEL_FORCE_MODE_MASK \ + ((1 << BASE_ACCEL) | (1 << LID_ACCEL)) + +#define CONFIG_LID_ANGLE_SENSOR_BASE BASE_ACCEL +#define CONFIG_LID_ANGLE_SENSOR_LID LID_ACCEL + +/* power signal definitions */ +enum power_signal { + X86_ALL_SYS_PWRGD = 0, + X86_RSMRST_L_PWRGD, + X86_SLP_S3_DEASSERTED, + X86_SLP_S4_DEASSERTED, + + /* Number of X86 signals */ + POWER_SIGNAL_COUNT +}; + +enum pwm_channel { + PWM_CH_LED_RED, + PWM_CH_LED_BLUE, + PWM_CH_LED_GREEN, + /* Number of PWM channels */ + PWM_CH_COUNT +}; + +enum temp_sensor_id { + /* TMP432 local and remote sensors */ + TEMP_SENSOR_I2C_TMP432_LOCAL, + TEMP_SENSOR_I2C_TMP432_REMOTE1, + TEMP_SENSOR_I2C_TMP432_REMOTE2, + + /* Battery temperature sensor */ + TEMP_SENSOR_BATTERY, + + TEMP_SENSOR_COUNT +}; + +/* Light sensors */ +enum als_id { + ALS_ISL29035 = 0, + + ALS_COUNT, +}; + +#endif /* !__ASSEMBLER__ */ + +#endif /* __CROS_EC_BOARD_H */ diff --git a/board/strago/build.mk b/board/strago/build.mk new file mode 100644 index 0000000000..894de2b191 --- /dev/null +++ b/board/strago/build.mk @@ -0,0 +1,14 @@ +# -*- makefile -*- +# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# Board specific files build +# + +# the IC is SMSC MEC1322 / external SPI is 4MB / external clock is crystal +CHIP:=mec1322 +CHIP_SPI_SIZE_KB:=512 + +board-y=board.o led.o +board-$(CONFIG_BATTERY_SMART)+=battery.o diff --git a/board/strago/ec.tasklist b/board/strago/ec.tasklist new file mode 100644 index 0000000000..036a6b8764 --- /dev/null +++ b/board/strago/ec.tasklist @@ -0,0 +1,29 @@ +/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/** + * List of enabled tasks in the priority order + * + * The first one has the lowest priority. + * + * For each task, use the macro TASK_ALWAYS(n, r, d, s) for base tasks and + * TASK_NOTEST(n, r, d, s) for tasks that can be excluded in test binaries, + * where : + * 'n' in the name of the task + * 'r' in the main routine of the task + * 'd' in an opaque parameter passed to the routine at startup + * 's' is the stack size in bytes; must be a multiple of 8 + */ +#define CONFIG_TASK_LIST \ + TASK_ALWAYS(HOOKS, hook_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(CHARGER, charger_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_NOTEST(MOTIONSENSE, motion_sense_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_NOTEST(CHIPSET, chipset_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \ + TASK_ALWAYS(HOSTCMD, host_command_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(CONSOLE, console_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_NOTEST(PORT80, port80_task, NULL, TASK_STACK_SIZE) \ + TASK_ALWAYS(POWERBTN, power_button_task, NULL, TASK_STACK_SIZE) \ + TASK_NOTEST(KEYSCAN, keyboard_scan_task, NULL, TASK_STACK_SIZE) diff --git a/board/strago/gpio.inc b/board/strago/gpio.inc new file mode 100644 index 0000000000..1a4addc539 --- /dev/null +++ b/board/strago/gpio.inc @@ -0,0 +1,150 @@ +/* -*- mode:c -*- + * + * Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +GPIO_INT(LID_OPEN, PIN(27), GPIO_INT_BOTH | GPIO_PULL_UP, lid_interrupt) /* Lid switch */ +GPIO_INT(AC_PRESENT, PIN(30), GPIO_INT_BOTH | GPIO_PULL_UP | GPIO_PULL_DOWN, extpower_interrupt) /* BC_ACOK / EC_ACIN - to know if battery or AC connected */ +#ifdef CONFIG_BUTTON_COUNT +GPIO_INT(VOLUME_UP, PIN(31), GPIO_INT_BOTH, button_interrupt) /* Volume up button */ +GPIO_INT(VOLUME_DOWN, PIN(34), GPIO_INT_BOTH, button_interrupt) /* Volume down button */ +#endif +GPIO_INT(POWER_BUTTON_L, PIN(35), GPIO_INT_BOTH, power_button_interrupt) /* Power button */ +GPIO_INT(RSMRST_L_PGOOD, PIN(63), GPIO_INT_BOTH, power_signal_interrupt) /* RSMRST_N_PWRGD from power logic */ +GPIO_INT(ALL_SYS_PGOOD, PIN(130), GPIO_INT_BOTH, power_signal_interrupt) /* ALL_SYS_PWRGD from power logic */ +#ifdef CONFIG_LOW_POWER_IDLE +GPIO_INT(UART0_RX, PIN(162), GPIO_INT_BOTH_DSLEEP | GPIO_PULL_UP, uart_deepsleep_interrupt) /* UART0 RX input */ +#else +GPIO_INT(UART0_RX, PIN(162), GPIO_INPUT | GPIO_PULL_UP, NULL) /* UART0 RX input */ +#endif +GPIO_INT(PCH_SLP_S4_L, PIN(200), GPIO_INT_BOTH, power_signal_interrupt) /* SLP_S4# signal from PCH */ +GPIO_INT(PCH_SLP_S3_L, PIN(206), GPIO_INT_BOTH, power_signal_interrupt) /* SLP_S3# signal from PCH */ + +GPIO(BOARD_VERSION2, PIN(0), GPIO_INPUT) /* BOARD_ID1 */ +GPIO(KBD_KSO2, PIN(1), GPIO_KB_OUTPUT_COL2) /* Negative edge triggered irq. */ + +GPIO(NC_USBPD_BOOT0, PIN(12), GPIO_INPUT | GPIO_PULL_DOWN) /* NC */ +GPIO(USB_ILIM_SEL, PIN(13), GPIO_OUT_HIGH) /* USB current control */ +GPIO(I2C_PORT0_0_SCL, PIN(15), GPIO_INPUT) +GPIO(I2C_PORT0_0_SDA, PIN(16), GPIO_INPUT) +GPIO(I2C_PORT0_1_SCL, PIN(134), GPIO_INPUT) +GPIO(I2C_PORT0_1_SDA, PIN(17), GPIO_INPUT) +GPIO(I2C_PORT1_SCL, PIN(22), GPIO_INPUT) +GPIO(I2C_PORT1_SDA, PIN(23), GPIO_INPUT) +GPIO(I2C_PORT2_SCL, PIN(20), GPIO_INPUT) +GPIO(I2C_PORT2_SDA, PIN(21), GPIO_INPUT) +GPIO(I2C_PORT3_SCL, PIN(24), GPIO_INPUT) +GPIO(I2C_PORT3_SDA, PIN(25), GPIO_INPUT) +GPIO(PCH_SCI_L, PIN(26), GPIO_ODR_HIGH) /* SCI output */ + +#ifndef CONFIG_BUTTON_COUNT +GPIO(VOLUME_UP, PIN(31), GPIO_INPUT) /* Volume up button */ +#endif +GPIO(WP_L, PIN(33), GPIO_INPUT) /* EC_SPI_WP_ME_L */ +#ifndef CONFIG_BUTTON_COUNT +GPIO(VOLUME_DOWN, PIN(34), GPIO_INPUT) /* Volume down button */ +#endif +GPIO(USB2_ENABLE, PIN(36), GPIO_OUT_LOW) /* Enable power for USB2 Port */ + +GPIO(ENTERING_RW, PIN(41), GPIO_OUT_LOW) /* Indicate when EC is entering RW code */ +GPIO(PCH_SMI_L, PIN(44), GPIO_ODR_HIGH) /* SMI output */ +GPIO(USB_OC1_L, PIN(45), GPIO_INT_FALLING) /* DB2 BC1.2 over current signal to EC */ +GPIO(DP_USB_C_HPD_Q, PIN(46), GPIO_OUT_HIGH) /* DP hot plug detect from EC to SOC */ + +GPIO(OTG_SW_EN, PIN(50), GPIO_OUT_LOW) /* */ +GPIO(PCH_SUS_STAT_L, PIN(51), GPIO_INT_FALLING) /* Signal to inform EC that SOC is entering low power state */ +GPIO(EC_ACDET_CTRL, PIN(52), GPIO_INPUT | GPIO_PULL_UP) +GPIO(TRACKPAD_PWREN, PIN(53), GPIO_OUT_HIGH) /* Enable power for Track Pad */ +GPIO(USB_OC0_L, PIN(55), GPIO_INT_FALLING) /* Over current signal of the BC1.2 charger to EC */ +GPIO(EC_ADC1, PIN(57), GPIO_INPUT) /* EC_ADC1, TEMP_SENSOR_2 no_stuff */ + +GPIO(EC_ADC0, PIN(61), GPIO_INPUT) /* EC_ADC0 */ +GPIO(EC_HIB_L, PIN(64), GPIO_OUT_LOW) /* Set to high before Pseduo G3 */ +GPIO(PCH_SYS_PWROK, PIN(65), GPIO_OUT_LOW) /* EC thinks everything is up and ready (DELAY_ALL_SYS_PWRGD) */ +GPIO(PCH_WAKE_L, PIN(66), GPIO_ODR_HIGH) /* PCH wake pin */ +GPIO(USB1_ENABLE, PIN(67), GPIO_OUT_LOW) /* Enable power for USB3 Port */ + +GPIO(NC_GPIO100, PIN(100), GPIO_INPUT | GPIO_PULL_UP) /* NC */ +GPIO(NC_GPIO101, PIN(101), GPIO_INPUT | GPIO_PULL_UP) /* NC */ +GPIO(BOARD_VERSION3, PIN(102), GPIO_INPUT) /* BOARD_ID2 */ +GPIO(USB_CTL1, PIN(105), GPIO_OUT_HIGH) /* USB charging mode control */ + +GPIO(PCH_RCIN_L, PIN(110), GPIO_ODR_HIGH) /* Reset line to PCH (for 8042 emulation) */ +GPIO(NC_115, PIN(115), GPIO_INPUT | GPIO_PULL_UP) /* NC */ +GPIO(EC_VNN_VCLK, PIN(122), GPIO_INPUT | GPIO_PULL_UP) /* Interrupt from USB PD Controller to EC */ +GPIO(STRAP_L, PIN(123), GPIO_OUT_LOW) +GPIO(EC_VNN_ALERT_L, PIN(124), GPIO_INPUT | GPIO_PULL_UP) +GPIO(GYRO_INT2, PIN(127), GPIO_INPUT | GPIO_PULL_DOWN) /* Gyro sensor interrupt 2 to EC */ + +GPIO(EC_PLUG_DETECT, PIN(132), GPIO_INT_BOTH | GPIO_PULL_UP | GPIO_PULL_DOWN) +GPIO(PD_RST_L, PIN(135), GPIO_ODR_HIGH) /* USB PD MCU reset */ + +GPIO(THERMAL_PROBE_EN_L,PIN(140), GPIO_OUT_HIGH) +GPIO(PCH_RSMRST_L, PIN(143), GPIO_OUT_LOW) /* RSMRST_N to PCH */ +GPIO(EC_KBD_ALERT, PIN(145), GPIO_OUT_LOW) /* EC_KBD_ALERT */ +GPIO(PVT_CS0, PIN(146), GPIO_ODR_HIGH) /* SPI PVT Chip select */ +GPIO(ALS_INT, PIN(147), GPIO_INPUT | GPIO_PULL_UP) /* ALS sensor interrupt to EC */ + +GPIO(WLAN_OFF_L, PIN(150), GPIO_ODR_HIGH) /* Wireless LAN */ +GPIO(CPU_PROCHOT, PIN(151), GPIO_OUT_LOW) +GPIO(KBD_IRQ_L, PIN(152), GPIO_ODR_HIGH) /* Negative edge triggered irq. */ +GPIO(BOARD_VERSION1, PIN(154), GPIO_INPUT) /* BOARD_ID0 */ +GPIO(EC_PERICOM_INT, PIN(155), GPIO_INT_BOTH) /* Pericom USB device interrupt */ +GPIO(PWR_BTN_SELECT, PIN(156), GPIO_ODR_HIGH) /* HIGH in clamshell mode and LOW in tablet mode */ +GPIO(PCH_SUSPWRDNACK, PIN(157), GPIO_INT_FALLING) /* PMC SUSPWRDNACK signal from SOC to EC */ + +GPIO(PCH_PWRBTN_L, PIN(160), GPIO_OUT_HIGH) /* Power button output to PCH */ +GPIO(GYRO_INT1, PIN(161), GPIO_INPUT | GPIO_PULL_DOWN) /* Gyro sensor interrupt 1 to EC */ +GPIO(VP9_CODEC_RESET_L, PIN(163), GPIO_OUT_LOW) /* VP9_CODEC_RESET_L, OUTPUT, ACTIVE LOW */ + +GPIO(STARTUP_LATCH_SET, PIN(201), GPIO_INPUT) /* Not used in BCRD2. Programmed as Input to fix EC power leakage in S3*/ +GPIO(EC_BL_DISABLE_L, PIN(202), GPIO_OUT_HIGH) /* EDP backligh disable signal from EC */ +GPIO(SMC_SHUTDOWN, PIN(203), GPIO_OUT_LOW) /* Shutdown signal from EC to power sequencing PLD */ +GPIO(USBPD_BST_OFF, PIN(204), GPIO_OUT_LOW) + +GPIO(BAT_PRESENT_L, PIN(210), GPIO_INPUT) /* HW detection signal from battery to EC */ +GPIO(GPIO_3_EC, PIN(211), GPIO_OUT_LOW) /* Sleep SOIX signal from SOC to EC */ + +/* Alternate functions GPIO definition */ +ALTERNATE(PIN_MASK(16, 0x24), 1, MODULE_UART, 0) /* UART0 */ + +ALTERNATE(PIN_MASK(1, 0x60), 2, MODULE_I2C, GPIO_OPEN_DRAIN) /* I2C0: Battery Charger */ +ALTERNATE(PIN_MASK(2, 0x3f), 2, MODULE_I2C, GPIO_OPEN_DRAIN) /* I2C1: Temp Sensor / I2C2: SOC / I2C3: VNN */ + +ALTERNATE(PIN_MASK(0, 0xfc), 3, MODULE_KEYBOARD_SCAN, GPIO_KB_OUTPUT) +ALTERNATE(PIN_MASK(1, 0x03), 3, MODULE_KEYBOARD_SCAN, GPIO_KB_OUTPUT) +ALTERNATE(PIN_MASK(10, 0xd8), 3, MODULE_KEYBOARD_SCAN, GPIO_KB_OUTPUT) +ALTERNATE(PIN_MASK(3, 0x04), 3, MODULE_KEYBOARD_SCAN, GPIO_KB_INPUT) +ALTERNATE(PIN_MASK(4, 0x0d), 3, MODULE_KEYBOARD_SCAN, GPIO_KB_INPUT) +ALTERNATE(PIN_MASK(12, 0x60), 2, MODULE_KEYBOARD_SCAN, GPIO_KB_INPUT) +ALTERNATE(PIN_MASK(14, 0x14), 3, MODULE_KEYBOARD_SCAN, GPIO_KB_INPUT) + +ALTERNATE(PIN_MASK(1, 0x10), 0, MODULE_LPC, GPIO_ODR_HIGH) /* 14: LPC CLKRUN - Program as GPIO for power saving */ +ALTERNATE(PIN_MASK(11, 0x9e), 1, MODULE_LPC, 0) /* 111~114:LAD[0:3], 117:PCI_CLK */ +ALTERNATE(PIN_MASK(11, 0x40), 1, MODULE_LPC, GPIO_INT_BOTH) /* 116: LRESET# */ +ALTERNATE(PIN_MASK(12, 0x01), 1, MODULE_LPC, 0) /* 120: LFRAME# */ + +ALTERNATE(PIN_MASK(5, 0x10), 1, MODULE_SPI, 0) +ALTERNATE(PIN_MASK(16, 0x10), 1, MODULE_SPI, 0) +ALTERNATE(PIN_MASK(15, 0x08), 1, MODULE_SPI, 0) /* 153: CLK */ + +ALTERNATE(PIN_MASK(13, 0x48), 1, MODULE_PWM_LED, GPIO_OUTPUT) /* 133: PWM0, 136: PWM1 */ +ALTERNATE(PIN_MASK(14, 0x02), 1, MODULE_PWM_LED, GPIO_OUTPUT) /* 141: PWM3 */ +ALTERNATE(PIN_MASK(5, 0x40), 1, MODULE_ADC, 0) /* 56: temperature sensor 1 */ +ALTERNATE(PIN_MASK(6, 0x05), 1, MODULE_ADC, 0) /* 60: PC_MON, 62: temperature sensor 3 */ + +/* Re-Config LPC Pins to GPIO Open Drain for SOC G3 (EC - POWER_G3) state */ +ALTERNATE(PIN_MASK(1, 0x10), 0, MODULE_GPIO, GPIO_ODR_HIGH) /* 14: LPC CLKRUN */ +ALTERNATE(PIN_MASK(11, 0x9e), 0, MODULE_GPIO, GPIO_ODR_HIGH) /* 111~114:LAD[0:3], 117:PCI_CLK */ +ALTERNATE(PIN_MASK(11, 0x40), 0, MODULE_GPIO, GPIO_ODR_HIGH) /* 116: LRESET# */ +ALTERNATE(PIN_MASK(12, 0x01), 0, MODULE_GPIO, GPIO_ODR_HIGH) /* 120: LFRAME# */ + +/* I2C pins */ +/* I2C0_0 CLK - GPIO015, I2C0_0 DAT - GPIO016, I2C0_1 DAT - GPIO017 */ +ALTERNATE(PIN_MASK(1, 0xe0), 2, MODULE_I2C, GPIO_ODR_HIGH) +/* I2C0_1 CLK - GPIO134 */ +ALTERNATE(PIN_MASK(13, 0x10), 2, MODULE_I2C, GPIO_ODR_HIGH) +/* I2C{1,2,3} CLK / DAT - GPIO022-GPIO025*/ +ALTERNATE(PIN_MASK(2, 0x3f), 2, MODULE_I2C, GPIO_ODR_HIGH) diff --git a/board/strago/led.c b/board/strago/led.c new file mode 100644 index 0000000000..74c5da3598 --- /dev/null +++ b/board/strago/led.c @@ -0,0 +1,210 @@ +/* Copyright 2015 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Power/Battery LED control for Strago + */ + +#include "charge_state.h" +#include "chipset.h" +#include "console.h" +#include "extpower.h" +#include "gpio.h" +#include "hooks.h" +#include "led_common.h" +#include "pwm.h" +#include "registers.h" +#include "util.h" + +#define CPRINTF(format, args...) cprintf(CC_PWM, format, ## args) +#define CPRINTS(format, args...) cprints(CC_PWM, format, ## args) + +#define LED_TOTAL_TICKS 16 +#define LED_ON_TICKS 4 + +static int led_debug; + +const enum ec_led_id supported_led_ids[] = { + EC_LED_ID_POWER_LED, EC_LED_ID_BATTERY_LED}; +const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids); + +enum led_color { + LED_OFF = 0, + LED_RED, + LED_AMBER, + LED_GREEN, + + /* Number of colors, not a color itself */ + LED_COLOR_COUNT +}; + +/* Brightness vs. color, in the order of off, red, amber, and green */ +static const uint8_t color_brightness[LED_COLOR_COUNT][3] = { + /* {Red, Blue, Green}, */ + [LED_OFF] = { 0, 0, 0}, + [LED_RED] = {100, 0, 0}, + [LED_AMBER] = { 75, 0, 10}, + [LED_GREEN] = { 0, 0, 100}, +}; + +/** + * Set LED color + * + * @param color Enumerated color value + */ +static void set_color(enum led_color color) +{ + pwm_set_duty(PWM_CH_LED_RED, color_brightness[color][0]); + pwm_set_duty(PWM_CH_LED_BLUE, color_brightness[color][1]); + pwm_set_duty(PWM_CH_LED_GREEN, color_brightness[color][2]); +} + +void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range) +{ + brightness_range[EC_LED_COLOR_RED] = 100; + brightness_range[EC_LED_COLOR_BLUE] = 100; + brightness_range[EC_LED_COLOR_GREEN] = 100; +} + +int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness) +{ + pwm_set_duty(PWM_CH_LED_RED, brightness[EC_LED_COLOR_RED]); + pwm_set_duty(PWM_CH_LED_BLUE, brightness[EC_LED_COLOR_BLUE]); + pwm_set_duty(PWM_CH_LED_GREEN, brightness[EC_LED_COLOR_GREEN]); + return EC_SUCCESS; +} + +static void strago_led_set_power(void) +{ + static int power_ticks; + static int previous_state_suspend; + + power_ticks++; + + if (chipset_in_state(CHIPSET_STATE_SUSPEND)) { + /* Reset ticks if entering suspend so LED turns amber + * as soon as possible. */ + if (!previous_state_suspend) + power_ticks = 0; + + /* Blink once every four seconds. */ + set_color( + (power_ticks % LED_TOTAL_TICKS) < LED_ON_TICKS ? + LED_AMBER : LED_OFF); + + previous_state_suspend = 1; + return; + } + + previous_state_suspend = 0; + + if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) + set_color(LED_OFF); + else if (chipset_in_state(CHIPSET_STATE_ON)) + set_color(LED_GREEN); +} + +static void strago_led_set_battery(void) +{ + static int battery_ticks; + + battery_ticks++; + + switch (charge_get_state()) { + case PWR_STATE_CHARGE: + set_color(LED_AMBER); + break; + case PWR_STATE_ERROR: + set_color(LED_RED); + break; + case PWR_STATE_CHARGE_NEAR_FULL: + case PWR_STATE_IDLE: /* External power connected in IDLE. */ + set_color(LED_GREEN); + break; + default: + /* Other states don't alter LED behavior */ + break; + } +} + +static void led_init(void) +{ + /* Configure GPIOs */ + gpio_config_module(MODULE_PWM_LED, 1); + + /* + * Enable PWMs and set to 0% duty cycle. If they're disabled, + * seems to ground the pins instead of letting them float. + */ + pwm_enable(PWM_CH_LED_RED, 1); + pwm_enable(PWM_CH_LED_GREEN, 1); + pwm_enable(PWM_CH_LED_BLUE, 1); + + set_color(LED_OFF); +} +DECLARE_HOOK(HOOK_INIT, led_init, HOOK_PRIO_DEFAULT); + +/** + * Called by hook task every 250 ms + */ +static void led_tick(void) +{ + if (led_debug) + return; + + if (extpower_is_present()) { + if (led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED)) { + strago_led_set_battery(); + return; + } + } else if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) { + strago_led_set_power(); + return; + } + + set_color(LED_OFF); +} +DECLARE_HOOK(HOOK_TICK, led_tick, HOOK_PRIO_DEFAULT); + +static void dump_pwm_channels(void) +{ + int ch; + for (ch = 0; ch < 4; ch++) { + CPRINTF("channel = %d\n", ch); + CPRINTF("0x%04X 0x%04X 0x%04X\n", + MEC1322_PWM_CFG(ch), + MEC1322_PWM_ON(ch), + MEC1322_PWM_OFF(ch)); + } +} +/******************************************************************/ +/* Console commands */ +static int command_led_color(int argc, char **argv) +{ + if (argc > 1) { + if (!strcasecmp(argv[1], "debug")) { + led_debug ^= 1; + CPRINTF("led_debug = %d\n", led_debug); + } else if (!strcasecmp(argv[1], "off")) { + set_color(LED_OFF); + } else if (!strcasecmp(argv[1], "red")) { + set_color(LED_RED); + } else if (!strcasecmp(argv[1], "green")) { + set_color(LED_GREEN); + } else if (!strcasecmp(argv[1], "amber")) { + set_color(LED_AMBER); + } else { + /* maybe handle charger_discharge_on_ac() too? */ + return EC_ERROR_PARAM1; + } + } + + if (led_debug == 1) + dump_pwm_channels(); + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(ledcolor, command_led_color, + "[debug|red|green|amber|off]", + "Change LED color", + NULL); + diff --git a/board/strago/lfw/gpio.inc b/board/strago/lfw/gpio.inc new file mode 100644 index 0000000000..122aff0cdf --- /dev/null +++ b/board/strago/lfw/gpio.inc @@ -0,0 +1,16 @@ +/* -*- mode:c -*- + * + * Copyright (c) 2014 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Minimal set of GPIOs needed for LFW loader + */ + +GPIO(PVT_CS0, PIN(146), GPIO_ODR_HIGH) /* SPI PVT Chip select */ + +/* Alternate functions GPIO definition */ +ALTERNATE(PIN_MASK(16, 0x24), 1, MODULE_UART, 0) /* UART0 */ +ALTERNATE(PIN_MASK(5, 0x10), 1, MODULE_SPI, 0) +ALTERNATE(PIN_MASK(16, 0x10), 1, MODULE_SPI, 0) +ALTERNATE(PIN_MASK(15, 0x08), 1, MODULE_SPI, 0) /* 153: CLK */ diff --git a/driver/gyro_l3gd20h.c b/driver/gyro_l3gd20h.c new file mode 100644 index 0000000000..0f63a93ced --- /dev/null +++ b/driver/gyro_l3gd20h.c @@ -0,0 +1,439 @@ +/* Copyright 2015 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/** + * L3GD20H gyro module for Chrome EC 3D digital gyroscope. + */ + +#include "accelgyro.h" +#include "common.h" +#include "console.h" +#include "driver/gyro_l3gd20h.h" +#include "hooks.h" +#include "i2c.h" +#include "task.h" +#include "util.h" + +#define CPUTS(outstr) cputs(CC_ACCEL, outstr) +#define CPRINTF(format, args...) cprintf(CC_ACCEL, format, ## args) + +/* + * Struct for pairing an engineering value with the register value for a + * parameter. + */ +struct gyro_param_pair { + int val; /* Value in engineering units. */ + int reg_val; /* Corresponding register value. */ +}; + +/* + * List of angular rate range values in +/-dps's + * and their associated register values. + */ +const struct gyro_param_pair dps_ranges[] = { + {245, L3GD20_DPS_SEL_245}, + {500, L3GD20_DPS_SEL_500}, + {2000, L3GD20_DPS_SEL_2000_0}, + {2000, L3GD20_DPS_SEL_2000_1} +}; + +static inline const struct gyro_param_pair *get_range_table( + enum motionsensor_type type, int *psize) +{ + if (psize) + *psize = ARRAY_SIZE(dps_ranges); + return dps_ranges; +} + +/* List of ODR values in mHz and their associated register values. */ +const struct gyro_param_pair gyro_odr[] = { + {0, L3GD20_ODR_PD | L3GD20_LOW_ODR_MASK}, + {12500, L3GD20_ODR_12_5HZ | L3GD20_ODR_PD_MASK | L3GD20_LOW_ODR_MASK}, + {25000, L3GD20_ODR_25HZ | L3GD20_ODR_PD_MASK | L3GD20_LOW_ODR_MASK}, + {50000, L3GD20_ODR_50HZ_0 | L3GD20_ODR_PD_MASK | L3GD20_LOW_ODR_MASK}, + {50000, L3GD20_ODR_50HZ_1 | L3GD20_ODR_PD_MASK | L3GD20_LOW_ODR_MASK}, + {100000, L3GD20_ODR_100HZ | L3GD20_ODR_PD_MASK}, + {200000, L3GD20_ODR_200HZ | L3GD20_ODR_PD_MASK}, + {400000, L3GD20_ODR_400HZ | L3GD20_ODR_PD_MASK}, + {800000, L3GD20_ODR_800HZ | L3GD20_ODR_PD_MASK}, +}; + +static inline const struct gyro_param_pair *get_odr_table( + enum motionsensor_type type, int *psize) +{ + if (psize) + *psize = ARRAY_SIZE(gyro_odr); + return gyro_odr; +} + +static inline int get_ctrl_reg(enum motionsensor_type type) +{ + return L3GD20_CTRL_REG1; +} + +static inline int get_xyz_reg(enum motionsensor_type type) +{ + return L3GD20_OUT_X_L | (1 << 7); +} + +/** + * @return reg value that matches the given engineering value passed in. + * The round_up flag is used to specify whether to round up or down. + * Note, this function always returns a valid reg value. If the request is + * outside the range of values, it returns the closest valid reg value. + */ +static int get_reg_val(const int eng_val, const int round_up, + const struct gyro_param_pair *pairs, const int size) +{ + int i; + for (i = 0; i < size - 1; i++) { + if (eng_val <= pairs[i].val) + break; + + if (eng_val < pairs[i+1].val) { + if (round_up) + i += 1; + break; + } + } + return pairs[i].reg_val; +} + +/** + * @return engineering value that matches the given reg val + */ +static int get_engineering_val(const int reg_val, + const struct gyro_param_pair *pairs, const int size) +{ + int i; + for (i = 0; i < size; i++) { + if (reg_val == pairs[i].reg_val) + break; + } + return pairs[i].val; +} + +/** + * Read register from Gyrometer. + */ +static inline int raw_read8(const int addr, const int reg, int *data_ptr) +{ + return i2c_read8(I2C_PORT_GYRO, addr, reg, data_ptr); +} + +/** + * Write register from Gyrometer. + */ +static inline int raw_write8(const int addr, const int reg, int data) +{ + return i2c_write8(I2C_PORT_GYRO, addr, reg, data); +} + +static int set_range(const struct motion_sensor_t *s, + int range, + int rnd) +{ + int ret, ctrl_val, range_tbl_size; + uint8_t ctrl_reg, reg_val; + const struct gyro_param_pair *ranges; + struct l3gd20_data *data = (struct l3gd20_data *)s->drv_data; + + ctrl_reg = L3GD20_CTRL_REG4; + ranges = get_range_table(s->type, &range_tbl_size); + + reg_val = get_reg_val(range, rnd, ranges, range_tbl_size); + + /* + * Lock Gyro resource to prevent another task from attempting + * to write Gyro parameters until we are done. + */ + mutex_lock(s->mutex); + + ret = raw_read8(s->i2c_addr, ctrl_reg, &ctrl_val); + if (ret != EC_SUCCESS) + goto gyro_cleanup; + + ctrl_val = (ctrl_val & ~L3GD20_RANGE_MASK) | reg_val; + ret = raw_write8(s->i2c_addr, ctrl_reg, ctrl_val); + + /* Now that we have set the range, update the driver's value. */ + if (ret == EC_SUCCESS) + data->base.range = get_engineering_val(reg_val, ranges, + range_tbl_size); + +gyro_cleanup: + mutex_unlock(s->mutex); + return EC_SUCCESS; +} + +static int get_range(const struct motion_sensor_t *s, int *range) +{ + struct l3gd20_data *data = (struct l3gd20_data *)s->drv_data; + + return data->base.range; +} + +static int set_resolution(const struct motion_sensor_t *s, + int res, + int rnd) +{ + /* Only one resolution, L3GD20_RESOLUTION, so nothing to do. */ + return EC_SUCCESS; +} + +static int get_resolution(const struct motion_sensor_t *s, + int *res) +{ + *res = L3GD20_RESOLUTION; + return EC_SUCCESS; +} + +static int set_data_rate(const struct motion_sensor_t *s, + int rate, + int rnd) +{ + int ret, val, odr_tbl_size; + uint8_t ctrl_reg, reg_val; + const struct gyro_param_pair *data_rates; + struct l3gd20_data *data = s->drv_data; + + ctrl_reg = get_ctrl_reg(s->type); + data_rates = get_odr_table(s->type, &odr_tbl_size); + reg_val = get_reg_val(rate, rnd, data_rates, odr_tbl_size); + + /* + * Lock gyro resource to prevent another task from attempting + * to write gyro parameters until we are done. + */ + mutex_lock(s->mutex); + + ret = raw_read8(s->i2c_addr, ctrl_reg, &val); + if (ret != EC_SUCCESS) + goto gyro_cleanup; + + val = (val & ~(L3GD20_ODR_MASK | L3GD20_ODR_PD_MASK)) | + (reg_val & ~L3GD20_LOW_ODR_MASK); + ret = raw_write8(s->i2c_addr, ctrl_reg, val); + + /* Now that we have set the odr, update the driver's value. */ + if (ret == EC_SUCCESS) + data->base.odr = get_engineering_val(reg_val, data_rates, + odr_tbl_size); + + ret = raw_read8(s->i2c_addr, L3GD20_LOW_ODR, &val); + if (ret != EC_SUCCESS) + goto gyro_cleanup; + + /* We need to clear low_ODR bit for higher data rates */ + if (reg_val & L3GD20_LOW_ODR_MASK) + val |= 1; + else + val &= ~1; + + ret = raw_write8(s->i2c_addr, L3GD20_LOW_ODR, val); + if (ret != EC_SUCCESS) + goto gyro_cleanup; + + /* CTRL_REG5 24h + * [7] low-power mode = 0; + * [6] fifo disabled = 0; + * [5] Stop on fth = 0; + * [4] High pass filter enable = 1; + * [3:2] int1_sel = 0; + * [1:0] out_sel = 1; + */ + ret = raw_read8(s->i2c_addr, L3GD20_CTRL_REG5, &val); + if (ret != EC_SUCCESS) + goto gyro_cleanup; + + val |= (1 << 4); /* high-pass filter enabled */ + val |= (1 << 0); /* data in data reg are high-pass filtered */ + ret = raw_write8(s->i2c_addr, L3GD20_CTRL_REG5, val); + if (ret != EC_SUCCESS) + goto gyro_cleanup; + + ret = raw_read8(s->i2c_addr, L3GD20_CTRL_REG2, &val); + if (ret != EC_SUCCESS) + goto gyro_cleanup; + + /* + * Table 25. High pass filter mode configuration + * Table 26. High pass filter cut off frequency configuration + */ + val &= 0xf0; + val |= 0x04; + ret = raw_write8(s->i2c_addr, L3GD20_CTRL_REG2, val); + +gyro_cleanup: + mutex_unlock(s->mutex); + return ret; +} + +static int get_data_rate(const struct motion_sensor_t *s, + int *rate) +{ + struct l3gd20_data *data = (struct l3gd20_data *)s->drv_data; + + *rate = data->base.odr; + return EC_SUCCESS; +} + +static int set_offset(const struct motion_sensor_t *s, + const int16_t *offset, + int16_t temp) +{ + /* temperature is ignored */ + struct l3gd20_data *data = s->drv_data; + data->offset[X] = offset[X]; + data->offset[Y] = offset[Y]; + data->offset[Z] = offset[Z]; + return EC_SUCCESS; +} + +static int get_offset(const struct motion_sensor_t *s, + int16_t *offset, + int16_t *temp) +{ + struct l3gd20_data *data = s->drv_data; + offset[X] = data->offset[X]; + offset[Y] = data->offset[Y]; + offset[Z] = data->offset[Z]; + *temp = EC_MOTION_SENSE_INVALID_CALIB_TEMP; + return EC_SUCCESS; +} + +#ifdef CONFIG_ACCEL_INTERRUPTS +static int set_interrupt(const struct motion_sensor_t *s, + unsigned int threshold) +{ + /* Currently unsupported. */ + return EC_ERROR_UNKNOWN; +} +#endif + +static int is_data_ready(const struct motion_sensor_t *s, int *ready) +{ + int ret, tmp; + + ret = raw_read8(s->i2c_addr, L3GD20_STATUS_REG, &tmp); + + if (ret != EC_SUCCESS) { + CPRINTF("[%T %s type:0x%X RS Error]", s->name, s->type); + return ret; + } + + *ready = (tmp & L3GD20_STS_ZYXDA_MASK) ? 1 : 0; + + return EC_SUCCESS; +} + +static int read(const struct motion_sensor_t *s, vector_3_t v) +{ + uint8_t raw[6]; + uint8_t xyz_reg; + int ret, range, i, tmp = 0; + struct l3gd20_data *data = s->drv_data; + + ret = is_data_ready(s, &tmp); + if (ret != EC_SUCCESS) + return ret; + + /* + * If sensor data is not ready, return the previous read data. + * Note: return success so that motion senor task can read again + * to get the latest updated sensor data quickly. + */ + if (!tmp) { + if (v != s->raw_xyz) + memcpy(v, s->raw_xyz, sizeof(s->raw_xyz)); + return EC_SUCCESS; + } + + xyz_reg = get_xyz_reg(s->type); + + /* Read 6 bytes starting at xyz_reg */ + i2c_lock(I2C_PORT_GYRO, 1); + ret = i2c_xfer(I2C_PORT_GYRO, s->i2c_addr, + &xyz_reg, 1, raw, 6, I2C_XFER_SINGLE); + i2c_lock(I2C_PORT_GYRO, 0); + + if (ret != EC_SUCCESS) { + CPRINTF("[%T %s type:0x%X RD XYZ Error]", + s->name, s->type); + return ret; + } + + get_range(s, &range); + for (i = X; i <= Z; i++) { + v[i] = ((int16_t)((raw[i * 2 + 1] << 8) | raw[i * 2])); + v[i] += (data->offset[i] << 5) / range; + } + if (*s->rot_standard_ref != NULL) + rotate(v, *s->rot_standard_ref, v); + + return EC_SUCCESS; +} + +static int init(const struct motion_sensor_t *s) +{ + int ret = 0, tmp; + + ret = raw_read8(s->i2c_addr, L3GD20_WHO_AM_I_REG, &tmp); + if (ret) + return EC_ERROR_UNKNOWN; + + if (tmp != L3GD20_WHO_AM_I) + return EC_ERROR_ACCESS_DENIED; + + /* All axes are enabled */ + ret = raw_write8(s->i2c_addr, L3GD20_CTRL_REG1, 0x0f); + if (ret) + return EC_ERROR_UNKNOWN; + + mutex_lock(s->mutex); + ret = raw_read8(s->i2c_addr, L3GD20_CTRL_REG4, &tmp); + if (ret) { + mutex_unlock(s->mutex); + return EC_ERROR_UNKNOWN; + } + + tmp |= L3GD20_BDU_ENABLE; + ret = raw_write8(s->i2c_addr, L3GD20_CTRL_REG4, tmp); + mutex_unlock(s->mutex); + if (ret) + return EC_ERROR_UNKNOWN; + + /* Config GYRO Range */ + ret = set_range(s, s->runtime_config.range, 1); + if (ret) + return EC_ERROR_UNKNOWN; + + /* Config GYRO ODR */ + ret = set_data_rate(s, s->runtime_config.odr, 1); + if (ret) + return EC_ERROR_UNKNOWN; + + CPRINTF("[%T %s: MS Done Init type:0x%X range:%d odr:%d]\n", + s->name, s->type, s->runtime_config.range, + s->runtime_config.odr); + return ret; +} + +const struct accelgyro_drv l3gd20h_drv = { + .init = init, + .read = read, + .set_range = set_range, + .get_range = get_range, + .set_resolution = set_resolution, + .get_resolution = get_resolution, + .set_data_rate = set_data_rate, + .get_data_rate = get_data_rate, + .set_offset = set_offset, + .get_offset = get_offset, + .perform_calib = NULL, +#ifdef CONFIG_ACCEL_INTERRUPTS + .set_interrupt = set_interrupt, +#endif +}; diff --git a/driver/gyro_l3gd20h.h b/driver/gyro_l3gd20h.h new file mode 100644 index 0000000000..6ff9fba044 --- /dev/null +++ b/driver/gyro_l3gd20h.h @@ -0,0 +1,102 @@ +/* Copyright 2015 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* L3GD20H gyro module for Chrome EC */ + +#ifndef __CROS_EC_GYRO_L3GD20H_H +#define __CROS_EC_GYRO_L3GD20H_H + +#include "accelgyro.h" +#include "task.h" + +/* + * 7-bit address is 110101Xb. Where 'X' is determined + * by the voltage on the ADDR pin. + */ +#define L3GD20_ADDR0 0xd4 +#define L3GD20_ADDR1 0xd6 + +/* who am I */ +#define L3GD20_WHO_AM_I 0xd7 + +/* Chip specific registers. */ +#define L3GD20_WHO_AM_I_REG 0x0f +#define L3GD20_CTRL_REG1 0x20 +#define L3GD20_CTRL_REG2 0x21 +#define L3GD20_CTRL_REG3 0x22 +#define L3GD20_CTRL_REG4 0x23 +#define L3GD20_CTRL_REG5 0x24 +#define L3GD20_CTRL_REFERENCE 0x25 +#define L3GD20_OUT_TEMP 0x26 +#define L3GD20_STATUS_REG 0x27 +#define L3GD20_OUT_X_L 0x28 +#define L3GD20_OUT_X_H 0x29 +#define L3GD20_OUT_Y_L 0x2a +#define L3GD20_OUT_Y_H 0x2b +#define L3GD20_OUT_Z_L 0x2c +#define L3GD20_OUT_Z_H 0x2d +#define L3GD20_FIFO_CTRL_REG 0x2e +#define L3GD20_FIFO_SRC_REG 0x2f +#define L3GD20_INT1_CFG 0x30 +#define L3GD20_INT1_SRC 0x31 +#define L3GD20_INT1_TSH_XH 0x32 +#define L3GD20_INT1_TSH_XL 0x33 +#define L3GD20_INT1_TSH_YH 0x34 +#define L3GD20_INT1_TSH_YL 0x35 +#define L3GD20_INT1_TSH_ZH 0x36 +#define L3GD20_INT1_TSH_ZL 0x37 +#define L3GD20_INT1_DURATION 0x38 +#define L3GD20_LOW_ODR 0x39 + +#define L3GD20_DPS_SEL_245 (0 << 4) +#define L3GD20_DPS_SEL_500 (1 << 4) +#define L3GD20_DPS_SEL_2000_0 (2 << 4) +#define L3GD20_DPS_SEL_2000_1 (3 << 4) + +#define L3GD20_ODR_PD (0 << 3) +#define L3GD20_ODR_12_5HZ (0 << 6) +#define L3GD20_ODR_25HZ (1 << 6) +#define L3GD20_ODR_50HZ_0 (2 << 6) +#define L3GD20_ODR_50HZ_1 (3 << 6) +#define L3GD20_ODR_100HZ (0 << 6) +#define L3GD20_ODR_200HZ (1 << 6) +#define L3GD20_ODR_400HZ (2 << 6) +#define L3GD20_ODR_800HZ (3 << 6) + +#define L3GD20_ODR_MASK (3 << 6) +#define L3GD20_STS_ZYXDA_MASK (1 << 3) +#define L3GD20_RANGE_MASK (3 << 4) +#define L3GD20_LOW_ODR_MASK (1 << 0) +#define L3GD20_ODR_PD_MASK (1 << 3) + +/* + * Register : STATUS_REG + * Address : 0X27 + */ +enum l3gd20_status { + L3GD20_STS_DOWN = 0x00, + L3GD20_STS_ZYXDA_UP = 0x08, +}; + +/* + * Register : CTRL_REG4 + * Address : 0X23 + * Bit Group Name: BDU + */ +enum l3gd20_bdu { + L3GD20_BDU_DISABLE = 0x00, + L3GD20_BDU_ENABLE = 0x80, +}; + +/* Sensor resolution in number of bits. This sensor has fixed resolution. */ +#define L3GD20_RESOLUTION 16 + +extern const struct accelgyro_drv l3gd20h_drv; +struct l3gd20_data { + struct motion_data_t base; + int16_t offset[3]; +}; + +#endif /* __CROS_EC_GYRO_L3GD20H_H */ -- cgit v1.2.1