/* * Copyright (C) Fourier Semiconductor Inc. 2016-2020. All rights reserved. */ #include "fsm_public.h" #include #include #include #include #include #include #include #include #include #ifdef CONFIG_OF #include #include #endif #if defined(CONFIG_REGULATOR) #include static struct regulator *g_fsm_vdd = NULL; #endif //#define FSM_VBAT_MONITOR static DEFINE_MUTEX(g_fsm_mutex); static struct device *g_fsm_pdev = NULL; /* customize configrature */ #include "fsm_firmware.c" #include "fsm_misc.c" #include "fsm_codec.c" void fsm_mutex_lock() { mutex_lock(&g_fsm_mutex); } void fsm_mutex_unlock() { mutex_unlock(&g_fsm_mutex); } int fsm_i2c_reg_read(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t *pVal) { struct i2c_msg msgs[2]; uint8_t retries = 0; uint8_t buffer[2]; int ret; if (!fsm_dev || !fsm_dev->i2c || !pVal) { return -EINVAL; } // write register address. msgs[0].addr = fsm_dev->i2c->addr; msgs[0].flags = 0; msgs[0].len = 1; msgs[0].buf = ® // read register buffer. msgs[1].addr = fsm_dev->i2c->addr; msgs[1].flags = I2C_M_RD; msgs[1].len = 2; msgs[1].buf = &buffer[0]; do { mutex_lock(&fsm_dev->i2c_lock); ret = i2c_transfer(fsm_dev->i2c->adapter, &msgs[0], ARRAY_SIZE(msgs)); mutex_unlock(&fsm_dev->i2c_lock); if (ret != ARRAY_SIZE(msgs)) { fsm_delay_ms(5); retries++; } } while (ret != ARRAY_SIZE(msgs) && retries < FSM_I2C_RETRY); if (ret != ARRAY_SIZE(msgs)) { pr_err("read %02x transfer error: %d", reg, ret); return -EIO; } *pVal = ((buffer[0] << 8) | buffer[1]); return 0; } int fsm_i2c_reg_write(fsm_dev_t *fsm_dev, uint8_t reg, uint16_t val) { struct i2c_msg msgs[1]; uint8_t retries = 0; uint8_t buffer[3]; int ret; if (!fsm_dev || !fsm_dev->i2c) { return -EINVAL; } buffer[0] = reg; buffer[1] = (val >> 8) & 0x00ff; buffer[2] = val & 0x00ff; msgs[0].addr = fsm_dev->i2c->addr; msgs[0].flags = 0; msgs[0].len = sizeof(buffer); msgs[0].buf = &buffer[0]; do { mutex_lock(&fsm_dev->i2c_lock); ret = i2c_transfer(fsm_dev->i2c->adapter, &msgs[0], ARRAY_SIZE(msgs)); mutex_unlock(&fsm_dev->i2c_lock); if (ret != ARRAY_SIZE(msgs)) { fsm_delay_ms(5); retries++; } } while (ret != ARRAY_SIZE(msgs) && retries < FSM_I2C_RETRY); if (ret != ARRAY_SIZE(msgs)) { pr_err("write %02x transfer error: %d", reg, ret); return -EIO; } return 0; } int fsm_i2c_bulkwrite(fsm_dev_t *fsm_dev, uint8_t reg, uint8_t *data, int len) { uint8_t retries = 0; uint8_t *buf; int size; int ret; if (!fsm_dev || !fsm_dev->i2c || !data) { return -EINVAL; } size = sizeof(uint8_t) + len; buf = (uint8_t *)fsm_alloc_mem(size); if (!buf) { pr_err("alloc memery failed"); return -ENOMEM; } buf[0] = reg; memcpy(&buf[1], data, len); do { mutex_lock(&fsm_dev->i2c_lock); ret = i2c_master_send(fsm_dev->i2c, buf, size); mutex_unlock(&fsm_dev->i2c_lock); if (ret < 0) { fsm_delay_ms(5); retries++; } else if (ret == size) { break; } } while (ret != size && retries < FSM_I2C_RETRY); fsm_free_mem((void **)&buf); if (ret != size) { pr_err("write %02x transfer error: %d", reg, ret); return -EIO; } return 0; } bool fsm_set_pdev(struct device *dev) { if (g_fsm_pdev == NULL || dev == NULL) { g_fsm_pdev = dev; // pr_debug("dev_name: %s", dev_name(dev)); return true; } return false; // already got device } struct device *fsm_get_pdev(void) { return g_fsm_pdev; } int fsm_vddd_on(struct device *dev) { fsm_config_t *cfg = fsm_get_config(); int ret = 0; if (!cfg || cfg->vddd_on) { return 0; } #if defined(CONFIG_REGULATOR) g_fsm_vdd = regulator_get(dev, "fsm_vddd"); if (IS_ERR(g_fsm_vdd) != 0) { pr_err("error getting fsm_vddd regulator"); ret = PTR_ERR(g_fsm_vdd); g_fsm_vdd = NULL; return ret; } pr_info("enable regulator"); regulator_set_voltage(g_fsm_vdd, 1800000, 1800000); ret = regulator_enable(g_fsm_vdd); if (ret < 0) { pr_err("enabling fsm_vddd failed: %d", ret); } #endif cfg->vddd_on = 1; fsm_delay_ms(10); return ret; } void fsm_vddd_off(void) { fsm_config_t *cfg = fsm_get_config(); if (!cfg || !cfg->vddd_on || cfg->dev_count > 0) { return; } #if defined(CONFIG_REGULATOR) if (g_fsm_vdd) { pr_info("disable regulator"); regulator_disable(g_fsm_vdd); regulator_put(g_fsm_vdd); g_fsm_vdd = NULL; } #endif cfg->vddd_on = 0; } void *fsm_devm_kstrdup(struct device *dev, void *buf, size_t size) { char *devm_buf = devm_kzalloc(dev, size + 1, GFP_KERNEL); if (!devm_buf) { return devm_buf; } memcpy(devm_buf, buf, size); return devm_buf; } static int fsm_ext_reset(fsm_dev_t *fsm_dev) { fsm_config_t *cfg = fsm_get_config(); uint16_t id = 0; int ret; if (!cfg || !fsm_dev) { return -EINVAL; } if (cfg->reset_chip) { return 0; } if (gpio_is_valid(fsm_dev->rst_gpio)) { gpio_set_value(fsm_dev->rst_gpio, 0); fsm_delay_ms(10); // mdelay gpio_set_value(fsm_dev->rst_gpio, 1); fsm_delay_ms(1); // mdelay cfg->reset_chip = true; } fsm_mutex_lock(); /* soft reset */ fsm_dev->addr = fsm_dev->i2c->addr; ret = fsm_reg_read(fsm_dev, 0x01, &id); if (ret && fsm_dev->addr != 0x34) { fsm_dev->i2c->addr = 0x34; ret = fsm_reg_read(fsm_dev, 0x01, &id); } if (!ret && LOW8(id) == FS1599_DEV_ID) { fsm_reg_write(fsm_dev, 0x10, 0x0002); fsm_delay_ms(15); } else { ret = -ENODEV; } fsm_dev->i2c->addr = fsm_dev->addr; fsm_dev->addr = 0; fsm_mutex_unlock(); return 0; } #ifdef FSM_VBAT_MONITOR #define VBAT_LEVEL_1 (3900) #define VBAT_LEVEL_2 (3800) #define VBAT_LEVEL_3 (3700) #define VBAT_LEVEL_4 (3600) struct power_supply *psy; struct delayed_work monitor_work; struct workqueue_struct *wq; int fsm_get_amp_tempr(int *amp_data) { union power_supply_propval psp = { 0 }; int tempr = FSM_DFT_AMB_TEMPR; int vbat = FSM_DFT_AMB_VBAT; if (amp_data == NULL) { return -EINVAL; } #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)) if (psy && psy->get_property) { // battery temperatrue psy->get_property(psy, POWER_SUPPLY_PROP_TEMP, &psp); tempr = DIV_ROUND_CLOSEST(psp.intval, 10); psy->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &psp); vbat = DIV_ROUND_CLOSEST(psp.intval, 1000); } #else if (psy && psy->desc && psy->desc->get_property) { // battery temperatrue psy->desc->get_property(psy, POWER_SUPPLY_PROP_TEMP, &psp); tempr = DIV_ROUND_CLOSEST(psp.intval, 10); psy->desc->get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &psp); vbat = DIV_ROUND_CLOSEST(psp.intval, 1000); } #endif pr_info("vbat:%d, tempr:%d", vbat, tempr); amp_data[0] = tempr; amp_data[1] = vbat; return 0; } int fsm_set_monitor(bool enable) { if (enable) { queue_delayed_work(wq, &monitor_work, 5*HZ); } else { if (delayed_work_pending(&monitor_work)) { cancel_delayed_work_sync(&monitor_work); } } return 0; } static void fsm_work_monitor(struct work_struct *work) { int amp_data[2] = {0}; fsm_mutex_lock(); #ifdef FSM_VBAT_MONITOR fsm_get_amp_tempr(amp_data); if(amp_data[0] > VBAT_LEVEL_1){ fsm_set_scene(0); }else if((amp_data[0] < VBAT_LEVEL_1)&&(amp_data[0] > VBAT_LEVEL_2)){ fsm_set_scene(1); }else if((amp_data[0] < VBAT_LEVEL_2)&&(amp_data[0] > VBAT_LEVEL_3)){ fsm_set_scene(2); }else if((amp_data[0] < VBAT_LEVEL_3)&&(amp_data[0] > VBAT_LEVEL_4)){ fsm_set_scene(3); }else{ fsm_set_scene(4); } #endif fsm_mutex_unlock(); /* reschedule */ queue_delayed_work(wq, &monitor_work, 5*HZ); } #endif #ifdef CONFIG_OF static int fsm_parse_dts(struct i2c_client *i2c, fsm_dev_t *fsm_dev) { struct device_node *np = i2c->dev.of_node; int ret; if (fsm_dev == NULL || np == NULL) { return -EINVAL; } fsm_dev->rst_gpio = of_get_named_gpio(np, "fsm,rst-gpio", 0); if (gpio_is_valid(fsm_dev->rst_gpio)) { ret = devm_gpio_request_one(&i2c->dev, fsm_dev->rst_gpio, GPIOF_OUT_INIT_LOW, "FS16XX_RST"); if (ret) return ret; } fsm_dev->irq_gpio = of_get_named_gpio(np, "fsm,irq-gpio", 0); if (gpio_is_valid(fsm_dev->irq_gpio)) { ret = devm_gpio_request_one(&i2c->dev, fsm_dev->irq_gpio, GPIOF_OUT_INIT_LOW, "FS16XX_IRQ"); if (ret) return ret; } ret = of_property_read_u32(np, "fsm,re25-dft", &fsm_dev->re25_dft); if (ret) { fsm_dev->re25_dft = 0; } pr_info("re25 default:%d", fsm_dev->re25_dft); return 0; } static struct of_device_id fsm_match_tbl[] = { { .compatible = "foursemi,fs15xx" }, {}, }; MODULE_DEVICE_TABLE(of, fsm_match_tbl); #endif static int fsm_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { fsm_config_t *cfg = fsm_get_config(); fsm_dev_t *fsm_dev; int ret; pr_debug("enter"); if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { dev_err(&i2c->dev, "check I2C_FUNC_I2C failed"); return -EIO; } fsm_dev = devm_kzalloc(&i2c->dev, sizeof(struct fsm_dev), GFP_KERNEL); if (fsm_dev == NULL) { dev_err(&i2c->dev, "alloc memory fialed"); return -ENOMEM; } memset(fsm_dev, 0, sizeof(struct fsm_dev)); mutex_init(&fsm_dev->i2c_lock); fsm_dev->i2c = i2c; #ifdef CONFIG_OF ret = fsm_parse_dts(i2c, fsm_dev); if (ret) { dev_err(&i2c->dev, "failed to parse DTS node"); } #endif #if defined(CONFIG_FSM_REGMAP) fsm_dev->regmap = fsm_regmap_i2c_init(i2c); if (fsm_dev->regmap == NULL) { devm_kfree(&i2c->dev, fsm_dev); dev_err(&i2c->dev, "regmap init failed"); return -EINVAL; } #endif fsm_vddd_on(&i2c->dev); fsm_ext_reset(fsm_dev); ret = fsm_probe(fsm_dev, i2c->addr); if (ret) { dev_err(&i2c->dev, "detect device failed"); #if defined(CONFIG_FSM_REGMAP) fsm_regmap_i2c_deinit(fsm_dev->regmap); #endif devm_kfree(&i2c->dev, fsm_dev); return ret; } fsm_dev->id = cfg->dev_count - 1; i2c_set_clientdata(i2c, fsm_dev); pr_addr(info, "index:%d", fsm_dev->id); #ifdef FSM_VBAT_MONITOR psy = power_supply_get_by_name("battery"); wq = create_singlethread_workqueue("fs16xx"); INIT_DELAYED_WORK(&monitor_work, fsm_work_monitor); #endif if(fsm_dev->id == 0) { // reigster only in the first device fsm_set_pdev(&i2c->dev); fsm_misc_init(); fsm_sysfs_init(&i2c->dev); // fsm_codec_register(&i2c->dev, fsm_dev->id); } dev_info(&i2c->dev, "i2c probe completed"); return 0; } static int fsm_i2c_remove(struct i2c_client *i2c) { fsm_dev_t *fsm_dev = i2c_get_clientdata(i2c); pr_debug("enter"); if (fsm_dev == NULL) { pr_err("bad parameter"); return -EINVAL; } #ifdef FSM_VBAT_MONITOR cancel_delayed_work_sync(&monitor_work); destroy_workqueue(wq); #endif #if defined(CONFIG_FSM_REGMAP) fsm_regmap_i2c_deinit(fsm_dev->regmap); #endif if (fsm_dev->id == 0) { // fsm_codec_unregister(&i2c->dev); fsm_sysfs_deinit(&i2c->dev); fsm_misc_deinit(); fsm_set_pdev(NULL); } fsm_remove(fsm_dev); fsm_vddd_off(); if (gpio_is_valid(fsm_dev->irq_gpio)) { devm_gpio_free(&i2c->dev, fsm_dev->irq_gpio); } if (gpio_is_valid(fsm_dev->rst_gpio)) { devm_gpio_free(&i2c->dev, fsm_dev->rst_gpio); } devm_kfree(&i2c->dev, fsm_dev); dev_info(&i2c->dev, "i2c removed"); return 0; } static const struct i2c_device_id fsm_i2c_id[] = { { FSM_DRV_NAME, 0 }, { } }; MODULE_DEVICE_TABLE(i2c, fsm_i2c_id); static struct i2c_driver fsm_i2c_driver = { .driver = { .name = FSM_DRV_NAME, .owner = THIS_MODULE, #ifdef CONFIG_OF .of_match_table = of_match_ptr(fsm_match_tbl), #endif }, .probe = fsm_i2c_probe, .remove = fsm_i2c_remove, .id_table = fsm_i2c_id, }; int exfsm_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { return fsm_i2c_probe(i2c, id); } EXPORT_SYMBOL(exfsm_i2c_probe); int exfsm_i2c_remove(struct i2c_client *i2c) { return fsm_i2c_remove(i2c); } EXPORT_SYMBOL(exfsm_i2c_remove); int fsm_i2c_init(void) { return i2c_add_driver(&fsm_i2c_driver); } void fsm_i2c_exit(void) { i2c_del_driver(&fsm_i2c_driver); } static int __init fsm_mod_init(void) { int ret; ret = fsm_i2c_init(); if (ret) { pr_err("init fail: %d", ret); return ret; } return 0; } static void __exit fsm_mod_exit(void) { fsm_i2c_exit(); } //module_i2c_driver(fsm_i2c_driver); module_init(fsm_mod_init); module_exit(fsm_mod_exit); MODULE_AUTHOR("FourSemi SW "); MODULE_DESCRIPTION("FourSemi Smart PA Driver"); MODULE_VERSION(FSM_CODE_VERSION); MODULE_ALIAS("foursemi:"FSM_DRV_NAME); MODULE_LICENSE("GPL");