// SPDX-License-Identifier: GPL-2.0-only /* * TI Bandgap temperature sensor driver * * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/ * Author: J Keerthy <[email protected]> * Author: Moiz Sonasath <[email protected]> * Couple of fixes, DT and MFD adaptation: * Eduardo Valentin <[email protected]> */ #include <linux/clk.h> #include <linux/cpu_pm.h> #include <linux/device.h> #include <linux/err.h> #include <linux/export.h> #include <linux/gpio/consumer.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/reboot.h> #include <linux/spinlock.h> #include <linux/sys_soc.h> #include <linux/types.h> #include "ti-bandgap.h" static int ti_bandgap_force_single_read(struct ti_bandgap *bgp, int id); #ifdef CONFIG_PM_SLEEP static int bandgap_omap_cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v); #endif /*** Helper functions to access registers and their bitfields ***/ /** * ti_bandgap_readl() - simple read helper function * @bgp: pointer to ti_bandgap structure * @reg: desired register (offset) to be read * * Helper function to read bandgap registers. It uses the io remapped area. * Return: the register value. */ static u32 ti_bandgap_readl(struct ti_bandgap *bgp, u32 reg) { … } /** * ti_bandgap_writel() - simple write helper function * @bgp: pointer to ti_bandgap structure * @val: desired register value to be written * @reg: desired register (offset) to be written * * Helper function to write bandgap registers. It uses the io remapped area. */ static void ti_bandgap_writel(struct ti_bandgap *bgp, u32 val, u32 reg) { … } /** * DOC: macro to update bits. * * RMW_BITS() - used to read, modify and update bandgap bitfields. * The value passed will be shifted. */ #define RMW_BITS(bgp, id, reg, mask, val) … /*** Basic helper functions ***/ /** * ti_bandgap_power() - controls the power state of a bandgap device * @bgp: pointer to ti_bandgap structure * @on: desired power state (1 - on, 0 - off) * * Used to power on/off a bandgap device instance. Only used on those * that features tempsoff bit. * * Return: 0 on success, -ENOTSUPP if tempsoff is not supported. */ static int ti_bandgap_power(struct ti_bandgap *bgp, bool on) { … } /** * ti_errata814_bandgap_read_temp() - helper function to read dra7 sensor temperature * @bgp: pointer to ti_bandgap structure * @reg: desired register (offset) to be read * * Function to read dra7 bandgap sensor temperature. This is done separately * so as to workaround the errata "Bandgap Temperature read Dtemp can be * corrupted" - Errata ID: i814". * Read accesses to registers listed below can be corrupted due to incorrect * resynchronization between clock domains. * Read access to registers below can be corrupted : * CTRL_CORE_DTEMP_MPU/GPU/CORE/DSPEVE/IVA_n (n = 0 to 4) * CTRL_CORE_TEMP_SENSOR_MPU/GPU/CORE/DSPEVE/IVA_n * * Return: the register value. */ static u32 ti_errata814_bandgap_read_temp(struct ti_bandgap *bgp, u32 reg) { … } /** * ti_bandgap_read_temp() - helper function to read sensor temperature * @bgp: pointer to ti_bandgap structure * @id: bandgap sensor id * * Function to concentrate the steps to read sensor temperature register. * This function is desired because, depending on bandgap device version, * it might be needed to freeze the bandgap state machine, before fetching * the register value. * * Return: temperature in ADC values. */ static u32 ti_bandgap_read_temp(struct ti_bandgap *bgp, int id) { … } /*** IRQ handlers ***/ /** * ti_bandgap_talert_irq_handler() - handles Temperature alert IRQs * @irq: IRQ number * @data: private data (struct ti_bandgap *) * * This is the Talert handler. Use it only if bandgap device features * HAS(TALERT). This handler goes over all sensors and checks their * conditions and acts accordingly. In case there are events pending, * it will reset the event mask to wait for the opposite event (next event). * Every time there is a new event, it will be reported to thermal layer. * * Return: IRQ_HANDLED */ static irqreturn_t ti_bandgap_talert_irq_handler(int irq, void *data) { … } /** * ti_bandgap_tshut_irq_handler() - handles Temperature shutdown signal * @irq: IRQ number * @data: private data (unused) * * This is the Tshut handler. Use it only if bandgap device features * HAS(TSHUT). If any sensor fires the Tshut signal, we simply shutdown * the system. * * Return: IRQ_HANDLED */ static irqreturn_t ti_bandgap_tshut_irq_handler(int irq, void *data) { … } /*** Helper functions which manipulate conversion ADC <-> mi Celsius ***/ /** * ti_bandgap_adc_to_mcelsius() - converts an ADC value to mCelsius scale * @bgp: struct ti_bandgap pointer * @adc_val: value in ADC representation * @t: address where to write the resulting temperature in mCelsius * * Simple conversion from ADC representation to mCelsius. In case the ADC value * is out of the ADC conv table range, it returns -ERANGE, 0 on success. * The conversion table is indexed by the ADC values. * * Return: 0 if conversion was successful, else -ERANGE in case the @adc_val * argument is out of the ADC conv table range. */ static int ti_bandgap_adc_to_mcelsius(struct ti_bandgap *bgp, int adc_val, int *t) { … } /** * ti_bandgap_validate() - helper to check the sanity of a struct ti_bandgap * @bgp: struct ti_bandgap pointer * @id: bandgap sensor id * * Checks if the bandgap pointer is valid and if the sensor id is also * applicable. * * Return: 0 if no errors, -EINVAL for invalid @bgp pointer or -ERANGE if * @id cannot index @bgp sensors. */ static inline int ti_bandgap_validate(struct ti_bandgap *bgp, int id) { … } /** * ti_bandgap_read_counter() - read the sensor counter * @bgp: pointer to bandgap instance * @id: sensor id * @interval: resulting update interval in miliseconds */ static void ti_bandgap_read_counter(struct ti_bandgap *bgp, int id, int *interval) { … } /** * ti_bandgap_read_counter_delay() - read the sensor counter delay * @bgp: pointer to bandgap instance * @id: sensor id * @interval: resulting update interval in miliseconds */ static void ti_bandgap_read_counter_delay(struct ti_bandgap *bgp, int id, int *interval) { … } /** * ti_bandgap_read_update_interval() - read the sensor update interval * @bgp: pointer to bandgap instance * @id: sensor id * @interval: resulting update interval in miliseconds * * Return: 0 on success or the proper error code */ int ti_bandgap_read_update_interval(struct ti_bandgap *bgp, int id, int *interval) { … } /** * ti_bandgap_write_counter_delay() - set the counter_delay * @bgp: pointer to bandgap instance * @id: sensor id * @interval: desired update interval in miliseconds * * Return: 0 on success or the proper error code */ static int ti_bandgap_write_counter_delay(struct ti_bandgap *bgp, int id, u32 interval) { … } /** * ti_bandgap_write_counter() - set the bandgap sensor counter * @bgp: pointer to bandgap instance * @id: sensor id * @interval: desired update interval in miliseconds */ static void ti_bandgap_write_counter(struct ti_bandgap *bgp, int id, u32 interval) { … } /** * ti_bandgap_write_update_interval() - set the update interval * @bgp: pointer to bandgap instance * @id: sensor id * @interval: desired update interval in miliseconds * * Return: 0 on success or the proper error code */ int ti_bandgap_write_update_interval(struct ti_bandgap *bgp, int id, u32 interval) { … } /** * ti_bandgap_read_temperature() - report current temperature * @bgp: pointer to bandgap instance * @id: sensor id * @temperature: resulting temperature * * Return: 0 on success or the proper error code */ int ti_bandgap_read_temperature(struct ti_bandgap *bgp, int id, int *temperature) { … } /** * ti_bandgap_set_sensor_data() - helper function to store thermal * framework related data. * @bgp: pointer to bandgap instance * @id: sensor id * @data: thermal framework related data to be stored * * Return: 0 on success or the proper error code */ int ti_bandgap_set_sensor_data(struct ti_bandgap *bgp, int id, void *data) { … } /** * ti_bandgap_get_sensor_data() - helper function to get thermal * framework related data. * @bgp: pointer to bandgap instance * @id: sensor id * * Return: data stored by set function with sensor id on success or NULL */ void *ti_bandgap_get_sensor_data(struct ti_bandgap *bgp, int id) { … } /*** Helper functions used during device initialization ***/ /** * ti_bandgap_force_single_read() - executes 1 single ADC conversion * @bgp: pointer to struct ti_bandgap * @id: sensor id which it is desired to read 1 temperature * * Used to initialize the conversion state machine and set it to a valid * state. Called during device initialization and context restore events. * * Return: 0 */ static int ti_bandgap_force_single_read(struct ti_bandgap *bgp, int id) { … } /** * ti_bandgap_set_continuous_mode() - One time enabling of continuous mode * @bgp: pointer to struct ti_bandgap * * Call this function only if HAS(MODE_CONFIG) is set. As this driver may * be used for junction temperature monitoring, it is desirable that the * sensors are operational all the time, so that alerts are generated * properly. * * Return: 0 */ static int ti_bandgap_set_continuous_mode(struct ti_bandgap *bgp) { … } /** * ti_bandgap_get_trend() - To fetch the temperature trend of a sensor * @bgp: pointer to struct ti_bandgap * @id: id of the individual sensor * @trend: Pointer to trend. * * This function needs to be called to fetch the temperature trend of a * Particular sensor. The function computes the difference in temperature * w.r.t time. For the bandgaps with built in history buffer the temperatures * are read from the buffer and for those without the Buffer -ENOTSUPP is * returned. * * Return: 0 if no error, else return corresponding error. If no * error then the trend value is passed on to trend parameter */ int ti_bandgap_get_trend(struct ti_bandgap *bgp, int id, int *trend) { … } /** * ti_bandgap_tshut_init() - setup and initialize tshut handling * @bgp: pointer to struct ti_bandgap * @pdev: pointer to device struct platform_device * * Call this function only in case the bandgap features HAS(TSHUT). * In this case, the driver needs to handle the TSHUT signal as an IRQ. * The IRQ is wired as a GPIO, and for this purpose, it is required * to specify which GPIO line is used. TSHUT IRQ is fired anytime * one of the bandgap sensors violates the TSHUT high/hot threshold. * And in that case, the system must go off. * * Return: 0 if no error, else error status */ static int ti_bandgap_tshut_init(struct ti_bandgap *bgp, struct platform_device *pdev) { … } /** * ti_bandgap_talert_init() - setup and initialize talert handling * @bgp: pointer to struct ti_bandgap * @pdev: pointer to device struct platform_device * * Call this function only in case the bandgap features HAS(TALERT). * In this case, the driver needs to handle the TALERT signals as an IRQs. * TALERT is a normal IRQ and it is fired any time thresholds (hot or cold) * are violated. In these situation, the driver must reprogram the thresholds, * accordingly to specified policy. * * Return: 0 if no error, else return corresponding error. */ static int ti_bandgap_talert_init(struct ti_bandgap *bgp, struct platform_device *pdev) { … } static const struct of_device_id of_ti_bandgap_match[]; /** * ti_bandgap_build() - parse DT and setup a struct ti_bandgap * @pdev: pointer to device struct platform_device * * Used to read the device tree properties accordingly to the bandgap * matching version. Based on bandgap version and its capabilities it * will build a struct ti_bandgap out of the required DT entries. * * Return: valid bandgap structure if successful, else returns ERR_PTR * return value must be verified with IS_ERR. */ static struct ti_bandgap *ti_bandgap_build(struct platform_device *pdev) { … } /* * List of SoCs on which the CPU PM notifier can cause erros on the DTEMP * readout. * Enabled notifier on these machines results in erroneous, random values which * could trigger unexpected thermal shutdown. */ static const struct soc_device_attribute soc_no_cpu_notifier[] = …; /*** Device driver call backs ***/ static int ti_bandgap_probe(struct platform_device *pdev) { … } static void ti_bandgap_remove(struct platform_device *pdev) { … } #ifdef CONFIG_PM_SLEEP static int ti_bandgap_save_ctxt(struct ti_bandgap *bgp) { … } static int ti_bandgap_restore_ctxt(struct ti_bandgap *bgp) { … } static int ti_bandgap_suspend(struct device *dev) { … } static int bandgap_omap_cpu_notifier(struct notifier_block *nb, unsigned long cmd, void *v) { … } static int ti_bandgap_resume(struct device *dev) { … } static SIMPLE_DEV_PM_OPS(ti_bandgap_dev_pm_ops, ti_bandgap_suspend, ti_bandgap_resume); #define DEV_PM_OPS … #else #define DEV_PM_OPS … #endif static const struct of_device_id of_ti_bandgap_match[] = …; MODULE_DEVICE_TABLE(of, of_ti_bandgap_match); static struct platform_driver ti_bandgap_sensor_driver = …; module_platform_driver(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …; MODULE_ALIAS(…) …; MODULE_AUTHOR(…) …;