On Thu, Apr 14, 2016 at 03:01:53PM +0530, Rajendra Nayak wrote: > 8960 family of SoCs have the TSENS device as part of GCC, hence > the driver probes the virtual child device created by GCC and > uses the parent to extract all DT properties and reuses the GCC > regmap. > > Also GCC/TSENS are part of a domain thats not always ON. > Hence add .suspend and .resume hooks to save and restore some of > the inited register context. > > Also 8960 family have some of the TSENS init sequence thats > required to be done by the HLOS driver (some later versions of TSENS > do not export these registers to non-secure world, and hence need > these initializations to be done by secure bootloaders) > > 8660 from the same family has just one sensor and hence some register > offset/layout differences which need special handling in the driver. > > Based on the original code from Siddartha Mohanadoss, Stephen Boyd and > Narendran Rajan. > > Signed-off-by: Rajendra Nayak <rnayak@xxxxxxxxxxxxxx> > --- > drivers/thermal/qcom/Makefile | 2 +- > drivers/thermal/qcom/tsens-8960.c | 293 ++++++++++++++++++++++++++++++++++++++ > drivers/thermal/qcom/tsens.c | 8 +- > drivers/thermal/qcom/tsens.h | 2 +- > 4 files changed, 299 insertions(+), 6 deletions(-) > create mode 100644 drivers/thermal/qcom/tsens-8960.c > > diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile > index a471100..f3cefd1 100644 > --- a/drivers/thermal/qcom/Makefile > +++ b/drivers/thermal/qcom/Makefile > @@ -1,2 +1,2 @@ > obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o > -qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o > +qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o > diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c > new file mode 100644 > index 0000000..a5976ec3 > --- /dev/null > +++ b/drivers/thermal/qcom/tsens-8960.c > @@ -0,0 +1,293 @@ > +/* > + * Copyright (c) 2015, The Linux Foundation. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 and > + * only version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > + > +#include <linux/platform_device.h> > +#include <linux/delay.h> > +#include <linux/bitops.h> > +#include <linux/regmap.h> > +#include <linux/thermal.h> > +#include "tsens.h" > + > +#define CAL_MDEGC 30000 > + > +#define CONFIG_ADDR 0x3640 > +#define CONFIG_ADDR_8660 0x3620 > +/* CONFIG_ADDR bitmasks */ > +#define CONFIG 0x9b > +#define CONFIG_MASK 0xf > +#define CONFIG_8660 1 > +#define CONFIG_SHIFT_8660 28 > +#define CONFIG_MASK_8660 (3 << CONFIG_SHIFT_8660) > + > +#define STATUS_CNTL_ADDR_8064 0x3660 > +#define CNTL_ADDR 0x3620 > +/* CNTL_ADDR bitmasks */ > +#define EN BIT(0) > +#define SW_RST BIT(1) > +#define SENSOR0_EN BIT(3) > +#define SLP_CLK_ENA BIT(26) > +#define SLP_CLK_ENA_8660 BIT(24) > +#define MEASURE_PERIOD 1 > +#define SENSOR0_SHIFT 3 > + > +/* INT_STATUS_ADDR bitmasks */ > +#define MIN_STATUS_MASK BIT(0) > +#define LOWER_STATUS_CLR BIT(1) > +#define UPPER_STATUS_CLR BIT(2) > +#define MAX_STATUS_MASK BIT(3) > + > +#define THRESHOLD_ADDR 0x3624 > +/* THRESHOLD_ADDR bitmasks */ > +#define THRESHOLD_MAX_LIMIT_SHIFT 24 > +#define THRESHOLD_MIN_LIMIT_SHIFT 16 > +#define THRESHOLD_UPPER_LIMIT_SHIFT 8 > +#define THRESHOLD_LOWER_LIMIT_SHIFT 0 > + > +/* Initial temperature threshold values */ > +#define LOWER_LIMIT_TH 0x50 > +#define UPPER_LIMIT_TH 0xdf > +#define MIN_LIMIT_TH 0x0 > +#define MAX_LIMIT_TH 0xff > + > +#define S0_STATUS_ADDR 0x3628 > +#define INT_STATUS_ADDR 0x363c > +#define TRDY_MASK BIT(7) > +#define TIMEOUT_US 100 > + > +static int suspend_8960(struct tsens_device *tmdev) > +{ > + int ret; > + unsigned int mask; > + struct regmap *map = tmdev->map; > + > + ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold); > + if (ret) > + return ret; > + > + ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control); > + if (ret) > + return ret; > + > + if (tmdev->num_sensors > 1) > + mask = SLP_CLK_ENA | EN; > + else > + mask = SLP_CLK_ENA_8660 | EN; > + > + ret = regmap_update_bits(map, CNTL_ADDR, mask, 0); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static int resume_8960(struct tsens_device *tmdev) > +{ > + int ret; > + struct regmap *map = tmdev->map; > + > + ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); > + if (ret) > + return ret; > + > + /* > + * Separate CONFIG restore is not needed only for 8660 as > + * config is part of CTRL Addr and its restored as such > + */ > + if (tmdev->num_sensors > 1) { > + ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); > + if (ret) > + return ret; > + } > + > + ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold); > + if (ret) > + return ret; > + > + ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static int enable_8960(struct tsens_device *tmdev, int id) > +{ > + int ret; > + u32 reg, mask; > + > + ret = regmap_read(tmdev->map, CNTL_ADDR, ®); > + if (ret) > + return ret; > + > + mask = BIT(id + SENSOR0_SHIFT); > + ret = regmap_write(tmdev->map, CNTL_ADDR, reg | SW_RST); > + if (ret) > + return ret; > + > + if (tmdev->num_sensors > 1) > + reg |= mask | SLP_CLK_ENA | EN; > + else > + reg |= mask | SLP_CLK_ENA_8660 | EN; > + > + ret = regmap_write(tmdev->map, CNTL_ADDR, reg); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static void disable_8960(struct tsens_device *tmdev) > +{ > + int ret; > + u32 reg_cntl; > + u32 mask; > + > + mask = GENMASK(tmdev->num_sensors - 1, 0); > + mask <<= SENSOR0_SHIFT; > + mask |= EN; > + > + ret = regmap_read(tmdev->map, CNTL_ADDR, ®_cntl); > + if (ret) > + return; > + > + reg_cntl &= ~mask; > + > + if (tmdev->num_sensors > 1) > + reg_cntl &= ~SLP_CLK_ENA; > + else > + reg_cntl &= ~SLP_CLK_ENA_8660; > + > + regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); > +} > + > +static int init_8960(struct tsens_device *tmdev) > +{ > + int ret, i; > + u32 reg_cntl; > + > + tmdev->map = dev_get_regmap(tmdev->dev, NULL); > + if (!tmdev->map) > + return -ENODEV; > + > + /* > + * The status registers for each sensor are discontiguous > + * because some SoCs have 5 sensors while others have more > + * but the control registers stay in the same place, i.e > + * directly after the first 5 status registers. > + */ > + for (i = 0; i < tmdev->num_sensors; i++) { > + if (i >= 5) > + tmdev->sensor[i].status = S0_STATUS_ADDR + 40; > + tmdev->sensor[i].status += i * 4; > + } > + > + reg_cntl = SW_RST; > + ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl); > + if (ret) > + return ret; > + > + if (tmdev->num_sensors > 1) { > + reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); > + reg_cntl &= ~SW_RST; > + ret = regmap_update_bits(tmdev->map, CONFIG_ADDR, > + CONFIG_MASK, CONFIG); > + } else { > + reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16); > + reg_cntl &= ~CONFIG_MASK_8660; > + reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660; > + } > + > + reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT; > + ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); > + if (ret) > + return ret; > + > + reg_cntl |= EN; > + ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static int calibrate_8960(struct tsens_device *tmdev) > +{ > + int i; > + char *data; > + > + ssize_t num_read = tmdev->num_sensors; > + struct tsens_sensor *s = tmdev->sensor; > + > + data = qfprom_read(tmdev->dev, "calib"); > + if (IS_ERR(data)) > + data = qfprom_read(tmdev->dev, "calib_backup"); > + if (IS_ERR(data)) > + return PTR_ERR(data); > + > + for (i = 0; i < num_read; i++, s++) > + s->offset = data[i]; > + > + return 0; > +} > + > +/* Temperature on y axis and ADC-code on x-axis */ > +static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) > +{ > + u32 slope; why unsigned? > + int offset; > + > + slope = thermal_zone_get_slope(s->tzd);; Please use one single ; > + offset = CAL_MDEGC - slope * s->offset; > + > + return adc_code * slope + offset; > +} > + > +static int get_temp_8960(struct tsens_device *tmdev, int id, int *temp) > +{ > + int ret; > + u32 code, trdy; > + const struct tsens_sensor *s = &tmdev->sensor[id]; > + unsigned long timeout; > + > + timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); > + do { > + ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy); > + if (ret) > + return ret; > + if (!(trdy & TRDY_MASK)) > + continue; > + ret = regmap_read(tmdev->map, s->status, &code); > + if (ret) > + return ret; > + *temp = code_to_mdegC(code, s); > + return 0; > + } while (time_before(jiffies, timeout)); > + > + return -ETIMEDOUT; > +} > + > +const struct tsens_ops ops_8960 = { > + .init = init_8960, > + .calibrate = calibrate_8960, > + .get_temp = get_temp_8960, > + .enable = enable_8960, > + .disable = disable_8960, > + .suspend = suspend_8960, > + .resume = resume_8960, > +}; > + > +const struct tsens_data data_8960 = { > + .num_sensors = 11, > + .ops = &ops_8960, > +}; > diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c > index 99f3a6a..096f64d 100644 > --- a/drivers/thermal/qcom/tsens.c > +++ b/drivers/thermal/qcom/tsens.c > @@ -122,10 +122,10 @@ static int tsens_probe(struct platform_device *pdev) > np = dev->of_node; > > id = of_match_node(tsens_table, np); > - if (!id) > - return -EINVAL; > - > - data = id->data; > + if (id) > + data = id->data; > + else > + data = &data_8960; > > if (data->num_sensors <= 0) { > dev_err(dev, "invalid number of sensors\n"); > diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h > index d4cfb5f..146d4f1 100644 > --- a/drivers/thermal/qcom/tsens.h > +++ b/drivers/thermal/qcom/tsens.h > @@ -87,6 +87,6 @@ void compute_intercept_slope(struct tsens_device *, u32 *, u32 *, u32); > int init_common(struct tsens_device *); > int get_temp_common(struct tsens_device *, int, int *); > > -extern const struct tsens_data data_8916, data_8974; > +extern const struct tsens_data data_8916, data_8974, data_8960; > > #endif /* __QCOM_TSENS_H__ */ > -- > QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member > of Code Aurora Forum, hosted by The Linux Foundation > -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html