- LK是little kernel的缩写
- 高通平台android普遍采用LK作为其bootloader,LK是一个开源项目。但是,LK只是整个系统的引导部分,所以它不是独立存在。LK是一个功能极其强大的bootloader,但只支持arm和x86平台。
- LK的一个显著的特点就是它实现了一个简单的线程机制(thread),和对高通处理器的深度定制和使用。
一、LK部分代码修改
开机电压检测,关机充电使能
1.1、bootable/bootloader/lk/app/aboot/aboot.c
#include <display_menu.h>
#include "fastboot_test.h"
+#include <i2c_qup.h>
+#include <blsp_qup.h>
+static struct qup_i2c_dev *i2c_dev;
void *info_buf;
void write_device_info_mmc(device_info *dev);
} else if (boot_reason_alarm) {
cmdline_len += strlen(alarmboot_cmdline);
- } else if ((target_build_variant_user() || device.charger_screen_enabled)
- && target_pause_for_battery_charge() && !boot_into_recovery) {
+ } else if (/*(target_build_variant_user() || device.charger_screen_enabled)
+ && */target_pause_for_battery_charge() && !boot_into_recovery) {
pause_at_bootup = 1;
cmdline_len += strlen(battchg_pause);
}
static int blsp_i2c_read_reg(uint8_t i2c_addr, uint8_t reg_addr, uint8_t *reg_data)
{
int ret = 0;
struct i2c_msg msg_buf[] = {
{i2c_addr, I2C_M_WR, 1, ®_addr},
{i2c_addr, I2C_M_RD, 1, reg_data}
};
ret = qup_i2c_xfer(i2c_dev, msg_buf, 2);
if (ret < 0) {
dprintf(CRITICAL, "qup_i2c_xfer read error %d, slave addr: %02x\n",
ret, i2c_addr);
return ret;
}
return ret;
}
static int blsp_i2c_write_reg(uint8_t i2c_addr, uint8_t reg_addr, uint8_t reg_data)
{
int ret = 0;
uint8_t buf[2];
buf[0] = reg_addr;
buf[1] = reg_data;
struct i2c_msg msg_buf[] = {
{i2c_addr, I2C_M_WR, sizeof(buf), buf},
};
ret = qup_i2c_xfer(i2c_dev, msg_buf, 1);
if (ret < 0) {
dprintf(CRITICAL, "qup_i2c_xfer write error %d, slave addr: %02x\n",
ret, i2c_addr);
return ret;
}
return ret;
}
static void battery_voltage_check_loop(void)
{
int ret;
uint8_t reg_sysmin = 0x03;
uint8_t reg_wdt = 0x07;
uint8_t reg_chip_id = 0x14;
uint8_t reg_vbat = 0x0E;
uint8_t reg_charger_online = 0x0B;
uint8_t reg_adc_convert = 0x02;
uint8_t chip_id, sysmin, wdt, adc_convert, vbat, charger_online;
uint32_t vbat_voltage, power_on_voltage = 3500;
gpio_tlmm_config(2, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA, GPIO_DISABLE);
//gpio_tlmm_config(137, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA, GPIO_DISABLE);
gpio_set_dir(2, 0); // enable charge chip enable
//gpio_set_dir(137, 2); // connect usb dpdm to sc60 module
i2c_dev = qup_blsp_i2c_init(BLSP_ID_1, QUP_ID_2, 100000, 19200000);
thread_sleep(100);
ret = blsp_i2c_read_reg(0x6A, reg_chip_id, &chip_id);
if (ret < 0 || ((chip_id >> 3) & 7) != 3) {
dprintf(CRITICAL, "get ba25890 chip id failed\n");
return;
}
// reset chip
ret = blsp_i2c_write_reg(0x6A, 0x14, 0x80);
if (ret < 0) { // any error will exit battery voltage check
dprintf(CRITICAL, "reset chip failed\n");
return;
}
thread_sleep(100);
ret = blsp_i2c_read_reg(0x6A, reg_wdt, &wdt);
if (ret < 0) { // any error will exit battery voltage check
dprintf(CRITICAL, "get wdt reg failed\n");
return;
}
wdt &= ~(3 << 4);
ret = blsp_i2c_write_reg(0x6A, reg_wdt, wdt);
if (ret < 0) { // any error will exit battery voltage check
dprintf(CRITICAL, "disable wdt failed\n");
return;
}
ret = blsp_i2c_read_reg(0x6A, reg_sysmin, &sysmin);
if (ret < 0) { // any error will exit battery voltage check
dprintf(CRITICAL, "get sysmin reg failed\n");
return;
}
sysmin &= ~(7 << 1);
ret = blsp_i2c_write_reg(0x6A, reg_sysmin, sysmin);
if (ret < 0) { // any error will exit battery voltage check
dprintf(CRITICAL, "set sysmin reg failed\n");
return;
}
ret = blsp_i2c_read_reg(0x6A, reg_adc_convert, &adc_convert);
if (ret < 0) { // any error will exit battery voltage check
dprintf(CRITICAL, "get adc convert reg failed\n");
return;
}
adc_convert |= 3 << 6;
ret = blsp_i2c_write_reg(0x6A, reg_adc_convert, adc_convert);
if (ret < 0) { // any error will exit battery voltage check
dprintf(CRITICAL, "enable adc convert failed\n");
return;
}
// wait adc start convert
thread_sleep(100);
while (1) {
ret = blsp_i2c_read_reg(0x6A, reg_vbat, &vbat);
if (ret < 0) {
dprintf(CRITICAL, "get vbat reg failed\n");
break;
}
ret = blsp_i2c_read_reg(0x6A, reg_charger_online, &charger_online);
if (ret < 0) { // any error will exit battery voltage check
dprintf(CRITICAL, "get charger_online reg failed\n");
break;
}
charger_online = (charger_online >> 2) & 1;
vbat_voltage = 2304 + (vbat & 0x7f) * 20;
// if charger online, decrease 50mV from battery voltage
if (charger_online)
vbat_voltage -= 50;
dprintf(CRITICAL, "battery voltage %d\n", vbat_voltage);
// if battery is absent, ntc will report cold, boot into android
if (vbat_voltage >= power_on_voltage)
break; // exit battery voltage check
else {
// sleep 1 minute, then recheck battery voltage
if (charger_online)
thread_sleep(3000);
else {
dprintf(CRITICAL, "battery voltage too low, shutdown device\n");
ret = blsp_i2c_write_reg(0x6A, 0x14, 0x80);
if (ret < 0) { // any error will exit battery voltage check
dprintf(CRITICAL, "reset chip failed\n");
break;
}
shutdown_device();
}
}
}
}
以下文件关于 I2C-2 使能
1.2、bootable/bootloader/lk/platform/msm8953/acpuclock.c
#include <clock.h>
#include <platform/clock.h>
#include <platform.h>
+#include <blsp_qup.h>
void clock_config_blsp_i2c(uint8_t blsp_id, uint8_t qup_id)
{
uint8_t ret = 0;
char clk_name[64];
struct clk *qup_clk;
snprintf(clk_name, sizeof(clk_name), "blsp1_ahb_clk");
ret = clk_get_set_enable(clk_name, 0 , 1);
if (ret) {
dprintf(CRITICAL, "Failed to enable %s clock\n", clk_name);
return;
}
snprintf(clk_name, sizeof(clk_name), "gcc_blsp1_qup3_i2c_apps_clk");
qup_clk = clk_get(clk_name);
if (!qup_clk) {
dprintf(CRITICAL, "Failed to get %s\n", clk_name);
return;
}
ret = clk_enable(qup_clk);
if (ret) {
dprintf(CRITICAL, "Failed to enable %s\n", clk_name);
return;
}
}
1.3、bootable/bootloader/lk/platform/msm8953/gpio.c
+
+void gpio_config_blsp_i2c(uint8_t blsp_id, uint8_t qup_id)
+{
+ /* configure I2C SDA gpio */
+ gpio_tlmm_config(10, 2, GPIO_OUTPUT, GPIO_NO_PULL,
+ GPIO_2MA, GPIO_DISABLE);
+
+ /* configure I2C SCL gpio */
+ gpio_tlmm_config(11, 2, GPIO_OUTPUT, GPIO_NO_PULL,
+ GPIO_2MA, GPIO_DISABLE);
+}
+
1.4、bootable/bootloader/lk/platform/msm8953/include/platform/clock.h
void clock_usb30_init(void);
void clock_reset_usb_phy();
+void clock_config_blsp_i2c(uint8_t blsp_id, uint8_t qup_id);
#endif
1.5、bootable/bootloader/lk/platform/msm8953/include/platform/gpio.h
void gpio_config_uart_dm(uint8_t id);
+void gpio_config_blsp_i2c(uint8_t blsp_id, uint8_t qup_id);
uint32_t gpio_status(uint32_t gpio);
1.6、bootable/bootloader/lk/platform/msm8953/include/platform/iomap.h
#define USB2_PHY_SEL 0x01937000
#define QUSB2_PHY_BASE 0X79000
+#define BLSP_QUP_BASE(blsp_id, qup_id) (PERIPH_SS_BASE + 0xB5000 + 0x1000 * qup_id)
+#define GCC_BLSP1_QUP3_APPS_CBCR (CLK_CTL_BASE + 0x04020)
+#define GCC_BLSP1_QUP3_CMD_RCGR (CLK_CTL_BASE + 0x04000)
+#define GCC_BLSP1_QUP3_CFG_RCGR (CLK_CTL_BASE + 0x04004)
/* SS QMP (Qulacomm Multi Protocol) */
#define QMP_PHY_BASE 0x78000
1.7、bootable/bootloader/lk/platform/msm8953/include/platform/irqs.h
#define SMD_IRQ (GIC_SPI_START + 168)
+#define BLSP_QUP_IRQ(blsp_id, qup_id) (GIC_SPI_START + 95 + qup_id)
#endif /* __IRQS_MSM8953_H */
1.8、bootable/bootloader/lk/platform/msm8953/msm8953-clock.c
/* I2C Clocks */
static struct clk_freq_tbl ftbl_gcc_blsp1_qup3_i2c_apps_clk_src[] =
{
F( 96000, cxo, 10, 1, 2),
F( 4800000, cxo, 4, 0, 0),
F( 9600000, cxo, 2, 0, 0),
F( 16000000, gpll0, 10, 1, 5),
F( 19200000, gpll0, 1, 0, 0),
F( 25000000, gpll0, 16, 1, 2),
F( 50000000, gpll0, 16, 0, 0),
F_END
};
static struct rcg_clk gcc_blsp1_qup3_i2c_apps_clk_src =
{
.cmd_reg = (uint32_t *) GCC_BLSP1_QUP3_CMD_RCGR,
.cfg_reg = (uint32_t *) GCC_BLSP1_QUP3_CFG_RCGR,
.set_rate = clock_lib2_rcg_set_rate_hid,
.freq_tbl = ftbl_gcc_blsp1_qup3_i2c_apps_clk_src,
.current_freq = &rcg_dummy_freq,
.c = {
.dbg_name = "gcc_blsp1_qup3_i2c_apps_clk_src",
.ops = &clk_ops_rcg,
},
};
static struct branch_clk gcc_blsp1_qup3_i2c_apps_clk = {
.cbcr_reg = (uint32_t *)GCC_BLSP1_QUP3_APPS_CBCR,
.parent = &gcc_blsp1_qup3_i2c_apps_clk_src.c,
.c = {
.dbg_name = "gcc_blsp1_qup3_i2c_apps_clk",
.ops = &clk_ops_branch,
},
};
/* UART Clocks */
static struct clk_lookup msm_clocks_8953[] =
{
CLK_LOOKUP("ce1_core_clk", gcc_ce1_clk.c),
CLK_LOOKUP("ce1_src_clk", ce1_clk_src.c),
+ CLK_LOOKUP("blsp1_ahb_clk", gcc_blsp1_ahb_clk.c),
+ CLK_LOOKUP("gcc_blsp1_qup3_i2c_apps_clk_src", gcc_blsp1_qup3_i2c_apps_clk_src.c),
+ CLK_LOOKUP("gcc_blsp1_qup3_i2c_apps_clk", gcc_blsp1_qup3_i2c_apps_clk.c),
};
1.9、bootable/bootloader/lk/platform/msm_shared/rules.mk
$(LOCAL_DIR)/mipi_dsc.o \
$(LOCAL_DIR)/mipi_dsi_phy.o \
+ $(LOCAL_DIR)/i2c_qup.o \
$(LOCAL_DIR)/mipi_dsi_autopll_thulium.o
endif
二、sys目录权限添加
2.1、device/qcom/sepolicy/vendor/msm8953/genfs_contexts
#PMI path
genfscon sysfs /devices/soc/qpnp-haptic-24/leds/vibrator u:object_r:sysfs_leds:s0
genfscon sysfs /devices/soc/qpnp-vadc-14/usbin u:object_r:sysfs_battery_supply:s0
+genfscon sysfs /devices/platform/soc/78b7000.i2c/i2c-3/3-0062/power_supply/bms u:object_r:sysfs_battery_supply:s0
+genfscon sysfs /devices/platform/soc/78b7000.i2c/i2c-3/3-006a/power_supply/pc_port u:object_r:sysfs_battery_supply:s0
+genfscon sysfs /devices/platform/soc/78b7000.i2c/i2c-3/3-006a/power_supply/usb u:object_r:sysfs_usb_supply:s0
+genfscon sysfs /devices/platform/soc/78b7000.i2c/i2c-3/3-006a/power_supply/main u:object_r:sysfs_usb_supply:s0
三、kernel内核驱动
3.1、添加 ChargerIC 和电量计配置
/kernel/msm-4.9/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
bq25890: bq25890@6a {
compatible = "ti,bq25890";
reg = <0x6a>;
ti,otg-charge = <1>;
ti,otg-sw-supply = <&pm8953_l5>;
ti,charge-enable-gpio = <&tlmm 2 0x00>;
ti,interrupt-gpio = <&tlmm 137 0x00>;
pinctrl-names = "default";
pinctrl-0 = <&bq25980_gpio_default>;
};
cw2015: cw2015@62 {
compatible = "cellwise,cw2015";
reg = <0x62>;
};
3.2、配置 ChargerIC 中断脚
kernel/msm-4.9/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
bq25980_gpio_default: bq25980_gpio_default {
mux {
pins = "gpio137";
function = "gpio";
};
config {
pins = "gpio137";
drive-strength = <2>;
bias-pull-up;
};
};
3.3、配置编译宏开关
kernel/msm-4.9/arch/arm64/configs/msm8953_defconfig
CONFIG_CRYPTO_CRC32_ARM64=y
CONFIG_QMI_ENCDEC=y
CONFIG_KEYBOARD_TCA8418=y
+CONFIG_CHARGER_BQ25890=y
+CONFIG_BATTERY_CW2015=y
3.4、配置编译宏开关
kernel/msm-4.9/drivers/power/supply/Makefile
obj-$(CONFIG_CHARGER_BQ24735) += bq24735-charger.o
obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o
+obj-$(CONFIG_BATTERY_CW2015) += cw2015_battery.o
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
3.5、ChargerIC 驱动
kernel/msm-4.9/drivers/power/supply/bq25890_charger.c
3.6、电量计驱动
kernel/msm-4.9/drivers/power/supply/xxxx_battery.c
四、关机充电显示电量百分比
4.1、/system/core/healthd/healthd_draw.cpp
#define STR_LEN 8
void HealthdDraw::draw_percent(const animation* anim) {
int cur_level = anim->cur_level;
if (anim->cur_status == BATTERY_STATUS_FULL) {
cur_level = 100;
}
if (cur_level <= 0) return;
const animation::text_field& field = anim->text_percent;
if (field.font == nullptr || field.font->char_width == 0 ||
field.font->char_height == 0) {
char cap_str[STR_LEN];
int x, y;
int str_len_px;
static int char_height = -1, char_width = -1;
if (char_height == -1 && char_width == -1)
gr_font_size(gr_sys_font(), &char_width, &char_height);
snprintf(cap_str, (STR_LEN - 1), "%d%%", cur_level);
str_len_px = gr_measure(gr_sys_font(), cap_str);
x = (gr_fb_width() - str_len_px) / 2;
y = (gr_fb_height() + char_height) / 2;
gr_color(0xa4, 0xc6, 0x39, 255);
gr_text(gr_sys_font(), x, y, cap_str, 0);
LOGE("draw_percent use default\n");
} else {
std::string str = base::StringPrintf("%d%%", cur_level);
int x, y;
determine_xy(field, str.size(), &x, &y);
LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);
gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
draw_text(field.font, x, y, str.c_str());
}
}