// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct max31760 { struct device *dev; u8 i2c_addr; struct regmap *regmap; u32 fan_pwr_en; u32 fan_pwr_bp; struct i2c_client *i2c_client; int pwm; bool fan_off; }; static void turn_gpio(struct max31760 *pdata, bool on) { if (on) { gpio_direction_output(pdata->fan_pwr_en, 0); gpio_set_value(pdata->fan_pwr_en, 1); pr_debug("%s gpio:%d set to high\n", __func__, pdata->fan_pwr_en); msleep(20); gpio_direction_output(pdata->fan_pwr_bp, 0); gpio_set_value(pdata->fan_pwr_bp, 1); pr_debug("%s gpio:%d set to high\n", __func__, pdata->fan_pwr_bp); msleep(20); } else { gpio_direction_output(pdata->fan_pwr_en, 1); gpio_set_value(pdata->fan_pwr_en, 0); pr_debug("%s gpio:%d set to low\n", __func__, pdata->fan_pwr_en); msleep(20); gpio_direction_output(pdata->fan_pwr_bp, 1); gpio_set_value(pdata->fan_pwr_bp, 0); pr_debug("%s gpio:%d set to low\n", __func__, pdata->fan_pwr_bp); msleep(20); } } static int max31760_i2c_reg_get(struct max31760 *pdata, u8 reg) { int ret; u32 val1; pr_debug("%s, reg:%x\n", __func__, reg); ret = regmap_read(pdata->regmap, (unsigned int)reg, &val1); if (ret < 0) { pr_err("%s failed reading reg 0x%02x failure\n", __func__, reg); return ret; } pr_debug("%s success reading reg 0x%x=0x%x, val1=%x\n", __func__, reg, val1, val1); return 0; } static int max31760_i2c_reg_set(struct max31760 *pdata, u8 reg, u8 val) { int ret; int i; for (i = 0; i < 10; i++) { ret = regmap_write(pdata->regmap, reg, val); if (ret >= 0) return ret; msleep(20); } if (ret < 0) pr_err("%s loop:%d failed to write reg 0x%02x=0x%02x\n", __func__, i, reg, val); return ret; } static ssize_t fan_show(struct device *dev, struct device_attribute *attr, char *buf) { struct max31760 *pdata; int ret; pdata = dev_get_drvdata(dev); if (!pdata) { pr_err("invalid driver pointer\n"); return -ENODEV; } if (pdata->fan_off) ret = scnprintf(buf, PAGE_SIZE, "off\n"); else ret = scnprintf(buf, PAGE_SIZE, "0x%x\n", pdata->pwm); return ret; } static ssize_t fan_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { long val, val1; struct max31760 *pdata; pdata = dev_get_drvdata(dev); if (!pdata) { pr_err("invalid driver pointer\n"); return -ENODEV; } kstrtol(buf, 0, &val); val1 = val >> 8; pr_debug("%s, count:%d val:%lx, val1:%lx, buf:%s\n", __func__, count, val, val1, buf); if (val1 == 0x50) { val1 = val & 0xFF; pr_debug("%s, reg value val1:%lx\n", __func__, val1); max31760_i2c_reg_set(pdata, 0x50, val1); return count; } if (val == 0xff) { turn_gpio(pdata, false); pdata->fan_off = true; } else if (val == 0xfe) { pdata->fan_off = false; turn_gpio(pdata, true); max31760_i2c_reg_set(pdata, 0x00, pdata->pwm); } else { max31760_i2c_reg_set(pdata, 0x00, (int)val); pdata->pwm = (int)val; } return count; } static DEVICE_ATTR_RW(fan); static struct attribute *max31760_fs_attrs[] = { &dev_attr_fan.attr, NULL }; static struct attribute_group max31760_fs_attr_group = { .attrs = max31760_fs_attrs, }; static int max31760_parse_dt(struct device *dev, struct max31760 *pdata) { struct device_node *np = dev->of_node; int ret; pdata->fan_pwr_en = of_get_named_gpio(np, "qcom,fan-pwr-en", 0); if (!gpio_is_valid(pdata->fan_pwr_en)) { pr_err("%s fan_pwr_en gpio not specified\n", __func__); ret = -EINVAL; } else { ret = gpio_request(pdata->fan_pwr_en, "fan_pwr_en"); if (ret) { pr_err("max31760 fan_pwr_en gpio request failed\n"); goto error1; } } pdata->fan_pwr_bp = of_get_named_gpio(np, "qcom,fan-pwr-bp", 0); if (!gpio_is_valid(pdata->fan_pwr_bp)) { pr_err("%s fan_pwr_bp gpio not specified\n", __func__); ret = -EINVAL; } else ret = gpio_request(pdata->fan_pwr_bp, "fan_pwr_bp"); if (ret) { pr_err("max31760 fan_pwr_bp gpio request failed\n"); goto error2; } turn_gpio(pdata, true); return ret; error2: gpio_free(pdata->fan_pwr_bp); error1: gpio_free(pdata->fan_pwr_en); return ret; } static int max31760_fan_pwr_enable_vregs(struct device *dev, struct max31760 *pdata) { int ret; struct regulator *reg; /* Fan Control LDO L10A */ reg = devm_regulator_get(dev, "pm8150_l10"); if (!IS_ERR(reg)) { regulator_set_load(reg, 600000); ret = regulator_enable(reg); if (ret < 0) { pr_err("%s pm8150_l10 failed\n", __func__); return -EINVAL; } } /* Fan Control LDO S4 */ reg = devm_regulator_get(dev, "pm8150_s4"); if (!IS_ERR(reg)) { regulator_set_load(reg, 600000); ret = regulator_enable(reg); if (ret < 0) { pr_err("%s pm8150_s4 failed\n", __func__); return -EINVAL; } } return ret; } static const struct regmap_config max31760_regmap = { .reg_bits = 8, .val_bits = 8, .max_register = 0xFF, }; static int max31760_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret; struct max31760 *pdata; if (!client || !client->dev.of_node) { pr_err("%s invalid input\n", __func__); return -EINVAL; } if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { pr_err("%s device doesn't support I2C\n", __func__); return -ENODEV; } pdata = devm_kzalloc(&client->dev, sizeof(struct max31760), GFP_KERNEL); if (!pdata) return -ENOMEM; pdata->regmap = devm_regmap_init_i2c(client, &max31760_regmap); if (IS_ERR(pdata->regmap)) { ret = PTR_ERR(pdata->regmap); pr_err("%s Failed to allocate regmap: %d\n", __func__, ret); return -EINVAL; } ret = max31760_parse_dt(&client->dev, pdata); if (ret) { pr_err("%s failed to parse device tree\n", __func__); return -EINVAL; } ret = max31760_fan_pwr_enable_vregs(&client->dev, pdata); if (ret) { pr_err("%s failed to pwr regulators\n", __func__); return -EINVAL; } pdata->dev = &client->dev; i2c_set_clientdata(client, pdata); pdata->i2c_client = client; dev_set_drvdata(&client->dev, pdata); ret = sysfs_create_group(&pdata->dev->kobj, &max31760_fs_attr_group); if (ret) pr_err("%s unable to register max31760 sysfs nodes\n"); /* 00 - 0x01 -- 33Hz */ /* 01 - 0x09 -- 150Hz */ /* 10 - 0x11 -- 1500Hz */ /* 11 - 0x19 -- 25Khz */ pdata->pwm = 0x19; max31760_i2c_reg_set(pdata, 0x00, pdata->pwm); max31760_i2c_reg_set(pdata, 0x01, 0x11); max31760_i2c_reg_set(pdata, 0x02, 0x31); max31760_i2c_reg_set(pdata, 0x03, 0x45); max31760_i2c_reg_set(pdata, 0x04, 0xff); max31760_i2c_reg_set(pdata, 0x50, 0xcf); max31760_i2c_reg_set(pdata, 0x01, 0x11); max31760_i2c_reg_set(pdata, 0x00, pdata->pwm); max31760_i2c_reg_get(pdata, 0x00); return ret; } static int max31760_remove(struct i2c_client *client) { struct max31760 *pdata = i2c_get_clientdata(client); if (!pdata) goto end; sysfs_remove_group(&pdata->dev->kobj, &max31760_fs_attr_group); turn_gpio(pdata, false); end: return 0; } static void max31760_shutdown(struct i2c_client *client) { } static int max31760_suspend(struct device *dev, pm_message_t state) { struct max31760 *pdata = dev_get_drvdata(dev); dev_dbg(dev, "suspend\n"); if (pdata) turn_gpio(pdata, false); return 0; } static int max31760_resume(struct device *dev) { struct max31760 *pdata = dev_get_drvdata(dev); dev_dbg(dev, "resume\n"); if (pdata) { turn_gpio(pdata, true); max31760_i2c_reg_set(pdata, 0x00, pdata->pwm); } return 0; } static const struct of_device_id max31760_id_table[] = { { .compatible = "maxim,xrfancontroller",}, { }, }; static const struct i2c_device_id max31760_i2c_table[] = { { "xrfancontroller", 0 }, { }, }; static struct i2c_driver max31760_i2c_driver = { .probe = max31760_probe, .remove = max31760_remove, .shutdown = max31760_shutdown, .driver = { .name = "maxim xrfancontroller", .of_match_table = max31760_id_table, .suspend = max31760_suspend, .resume = max31760_resume, }, .id_table = max31760_i2c_table, }; module_i2c_driver(max31760_i2c_driver); MODULE_DEVICE_TABLE(i2c, max31760_i2c_table); MODULE_DESCRIPTION("Maxim 31760 Fan Controller"); MODULE_LICENSE("GPL v2");