// SPDX-License-Identifier: GPL-2.0-or-later /* * g762 - Driver for the Global Mixed-mode Technology Inc. fan speed * PWM controller chips from G762 family, i.e. G762 and G763 * * Copyright (C) 2013, Arnaud EBALARD <[email protected]> * * This work is based on a basic version for 2.6.31 kernel developed * by Olivier Mouchet for LaCie. Updates and correction have been * performed to run on recent kernels. Additional features, like the * ability to configure various characteristics via .dts file or * board init file have been added. Detailed datasheet on which this * development is based is available here: * * http://natisbad.org/NAS/refs/GMT_EDS-762_763-080710-0.2.pdf * * Headers from previous developments have been kept below: * * Copyright (c) 2009 LaCie * * Author: Olivier Mouchet <[email protected]> * * based on g760a code written by Herbert Valerio Riedel <[email protected]> * Copyright (C) 2007 Herbert Valerio Riedel <[email protected]> * * g762: minimal datasheet available at: * http://www.gmt.com.tw/product/datasheet/EDS-762_3.pdf */ #include <linux/device.h> #include <linux/module.h> #include <linux/init.h> #include <linux/jiffies.h> #include <linux/i2c.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> #include <linux/kernel.h> #include <linux/clk.h> #include <linux/of.h> #include <linux/platform_data/g762.h> #define DRVNAME … static const struct i2c_device_id g762_id[] = …; MODULE_DEVICE_TABLE(i2c, g762_id); enum g762_regs { … }; /* Config register bits */ #define G762_REG_FAN_CMD1_DET_FAN_FAIL … #define G762_REG_FAN_CMD1_DET_FAN_OOC … #define G762_REG_FAN_CMD1_OUT_MODE … #define G762_REG_FAN_CMD1_FAN_MODE … #define G762_REG_FAN_CMD1_CLK_DIV_ID1 … #define G762_REG_FAN_CMD1_CLK_DIV_ID0 … #define G762_REG_FAN_CMD1_PWM_POLARITY … #define G762_REG_FAN_CMD1_PULSE_PER_REV … #define G761_REG_FAN_CMD2_FAN_CLOCK … #define G762_REG_FAN_CMD2_GEAR_MODE_1 … #define G762_REG_FAN_CMD2_GEAR_MODE_0 … #define G762_REG_FAN_CMD2_FAN_STARTV_1 … #define G762_REG_FAN_CMD2_FAN_STARTV_0 … #define G762_REG_FAN_STA_FAIL … #define G762_REG_FAN_STA_OOC … /* Config register values */ #define G762_OUT_MODE_PWM … #define G762_OUT_MODE_DC … #define G762_FAN_MODE_CLOSED_LOOP … #define G762_FAN_MODE_OPEN_LOOP … #define G762_PWM_POLARITY_NEGATIVE … #define G762_PWM_POLARITY_POSITIVE … /* Register data is read (and cached) at most once per second. */ #define G762_UPDATE_INTERVAL … /* * Extract pulse count per fan revolution value (2 or 4) from given * FAN_CMD1 register value. */ #define G762_PULSE_FROM_REG(reg) … /* * Extract fan clock divisor (1, 2, 4 or 8) from given FAN_CMD1 * register value. */ #define G762_CLKDIV_FROM_REG(reg) … /* * Extract fan gear mode multiplier value (0, 2 or 4) from given * FAN_CMD2 register value. */ #define G762_GEARMULT_FROM_REG(reg) … struct g762_data { … }; /* * Convert count value from fan controller register (FAN_SET_CNT) into fan * speed RPM value. Note that the datasheet documents a basic formula; * influence of additional parameters (fan clock divisor, fan gear mode) * have been infered from examples in the datasheet and tests. */ static inline unsigned int rpm_from_cnt(u8 cnt, u32 clk_freq, u16 p, u8 clk_div, u8 gear_mult) { … } /* * Convert fan RPM value from sysfs into count value for fan controller * register (FAN_SET_CNT). */ static inline unsigned char cnt_from_rpm(unsigned long rpm, u32 clk_freq, u16 p, u8 clk_div, u8 gear_mult) { … } /* helper to grab and cache data, at most one time per second */ static struct g762_data *g762_update_client(struct device *dev) { … } /* helpers for writing hardware parameters */ /* * Set input clock frequency received on CLK pin of the chip. Accepted values * are between 0 and 0xffffff. If zero is given, then default frequency * (32,768Hz) is used. Note that clock frequency is a characteristic of the * system but an internal parameter, i.e. value is not passed to the device. */ static int do_set_clk_freq(struct device *dev, unsigned long val) { … } /* Set pwm mode. Accepts either 0 (PWM mode) or 1 (DC mode) */ static int do_set_pwm_mode(struct device *dev, unsigned long val) { … } /* Set fan clock divisor. Accepts either 1, 2, 4 or 8. */ static int do_set_fan_div(struct device *dev, unsigned long val) { … } /* Set fan gear mode. Accepts either 0, 1 or 2. */ static int do_set_fan_gear_mode(struct device *dev, unsigned long val) { … } /* Set number of fan pulses per revolution. Accepts either 2 or 4. */ static int do_set_fan_pulses(struct device *dev, unsigned long val) { … } /* Set fan mode. Accepts either 1 (open-loop) or 2 (closed-loop). */ static int do_set_pwm_enable(struct device *dev, unsigned long val) { … } /* Set PWM polarity. Accepts either 0 (positive duty) or 1 (negative duty) */ static int do_set_pwm_polarity(struct device *dev, unsigned long val) { … } /* * Set pwm value. Accepts values between 0 (stops the fan) and * 255 (full speed). This only makes sense in open-loop mode. */ static int do_set_pwm(struct device *dev, unsigned long val) { … } /* * Set fan RPM value. Can be called both in closed and open-loop mode * but effect will only be seen after closed-loop mode is configured. */ static int do_set_fan_target(struct device *dev, unsigned long val) { … } /* Set fan startup voltage. Accepted values are either 0, 1, 2 or 3. */ static int do_set_fan_startv(struct device *dev, unsigned long val) { … } /* * Helper to import hardware characteristics from .dts file and push * those to the chip. */ #ifdef CONFIG_OF static const struct of_device_id g762_dt_match[] = …; MODULE_DEVICE_TABLE(of, g762_dt_match); /* * Grab clock (a required property), enable it, get (fixed) clock frequency * and store it. Note: upon success, clock has been prepared and enabled; it * must later be unprepared and disabled (e.g. during module unloading) by a * call to g762_of_clock_disable(). Note that a reference to clock is kept * in our private data structure to be used in this function. */ static void g762_of_clock_disable(void *data) { … } static int g762_of_clock_enable(struct i2c_client *client) { … } static int g762_of_prop_import_one(struct i2c_client *client, const char *pname, int (*psetter)(struct device *dev, unsigned long val)) { … } static int g762_of_prop_import(struct i2c_client *client) { … } #else static int g762_of_prop_import(struct i2c_client *client) { return 0; } static int g762_of_clock_enable(struct i2c_client *client) { return 0; } #endif /* * Helper to import hardware characteristics from .dts file and push * those to the chip. */ static int g762_pdata_prop_import(struct i2c_client *client) { … } /* * sysfs attributes */ /* * Read function for fan1_input sysfs file. Return current fan RPM value, or * 0 if fan is out of control. */ static ssize_t fan1_input_show(struct device *dev, struct device_attribute *da, char *buf) { … } /* * Read and write functions for pwm1_mode sysfs file. Get and set fan speed * control mode i.e. PWM (1) or DC (0). */ static ssize_t pwm1_mode_show(struct device *dev, struct device_attribute *da, char *buf) { … } static ssize_t pwm1_mode_store(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { … } /* * Read and write functions for fan1_div sysfs file. Get and set fan * controller prescaler value */ static ssize_t fan1_div_show(struct device *dev, struct device_attribute *da, char *buf) { … } static ssize_t fan1_div_store(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { … } /* * Read and write functions for fan1_pulses sysfs file. Get and set number * of tachometer pulses per fan revolution. */ static ssize_t fan1_pulses_show(struct device *dev, struct device_attribute *da, char *buf) { … } static ssize_t fan1_pulses_store(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { … } /* * Read and write functions for pwm1_enable. Get and set fan speed control mode * (i.e. closed or open-loop). * * Following documentation about hwmon's sysfs interface, a pwm1_enable node * should accept the following: * * 0 : no fan speed control (i.e. fan at full speed) * 1 : manual fan speed control enabled (use pwm[1-*]) (open-loop) * 2+: automatic fan speed control enabled (use fan[1-*]_target) (closed-loop) * * but we do not accept 0 as this mode is not natively supported by the chip * and it is not emulated by g762 driver. -EINVAL is returned in this case. */ static ssize_t pwm1_enable_show(struct device *dev, struct device_attribute *da, char *buf) { … } static ssize_t pwm1_enable_store(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { … } /* * Read and write functions for pwm1 sysfs file. Get and set pwm value * (which affects fan speed) in open-loop mode. 0 stops the fan and 255 * makes it run at full speed. */ static ssize_t pwm1_show(struct device *dev, struct device_attribute *da, char *buf) { … } static ssize_t pwm1_store(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { … } /* * Read and write function for fan1_target sysfs file. Get/set the fan speed in * closed-loop mode. Speed is given as a RPM value; then the chip will regulate * the fan speed using pulses from fan tachometer. * * Refer to rpm_from_cnt() implementation above to get info about count number * calculation. * * Also note that due to rounding errors it is possible that you don't read * back exactly the value you have set. */ static ssize_t fan1_target_show(struct device *dev, struct device_attribute *da, char *buf) { … } static ssize_t fan1_target_store(struct device *dev, struct device_attribute *da, const char *buf, size_t count) { … } /* read function for fan1_fault sysfs file. */ static ssize_t fan1_fault_show(struct device *dev, struct device_attribute *da, char *buf) { … } /* * read function for fan1_alarm sysfs file. Note that OOC condition is * enabled low */ static ssize_t fan1_alarm_show(struct device *dev, struct device_attribute *da, char *buf) { … } static DEVICE_ATTR_RW(pwm1); static DEVICE_ATTR_RW(pwm1_mode); static DEVICE_ATTR_RW(pwm1_enable); static DEVICE_ATTR_RO(fan1_input); static DEVICE_ATTR_RO(fan1_alarm); static DEVICE_ATTR_RO(fan1_fault); static DEVICE_ATTR_RW(fan1_target); static DEVICE_ATTR_RW(fan1_div); static DEVICE_ATTR_RW(fan1_pulses); /* Driver data */ static struct attribute *g762_attrs[] = …; ATTRIBUTE_GROUPS(…); /* * Enable both fan failure detection and fan out of control protection. The * function does not protect change/access to data structure; it must thus * only be called during initialization. */ static inline int g762_fan_init(struct device *dev) { … } static int g762_probe(struct i2c_client *client) { … } static struct i2c_driver g762_driver = …; module_i2c_driver(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;