| // SPDX-License-Identifier: GPL-2.0 | 
 | /* | 
 |  * Copyright (C) 2020 MaxLinear, Inc. | 
 |  * | 
 |  * This driver is a hardware monitoring driver for PVT controller | 
 |  * (MR75203) which is used to configure & control Moortec embedded | 
 |  * analog IP to enable multiple embedded temperature sensor(TS), | 
 |  * voltage monitor(VM) & process detector(PD) modules. | 
 |  */ | 
 | #include <linux/bits.h> | 
 | #include <linux/clk.h> | 
 | #include <linux/hwmon.h> | 
 | #include <linux/module.h> | 
 | #include <linux/mod_devicetable.h> | 
 | #include <linux/mutex.h> | 
 | #include <linux/platform_device.h> | 
 | #include <linux/property.h> | 
 | #include <linux/regmap.h> | 
 | #include <linux/reset.h> | 
 | #include <linux/units.h> | 
 |  | 
 | /* PVT Common register */ | 
 | #define PVT_IP_CONFIG	0x04 | 
 | #define TS_NUM_MSK	GENMASK(4, 0) | 
 | #define TS_NUM_SFT	0 | 
 | #define PD_NUM_MSK	GENMASK(12, 8) | 
 | #define PD_NUM_SFT	8 | 
 | #define VM_NUM_MSK	GENMASK(20, 16) | 
 | #define VM_NUM_SFT	16 | 
 | #define CH_NUM_MSK	GENMASK(31, 24) | 
 | #define CH_NUM_SFT	24 | 
 |  | 
 | /* Macro Common Register */ | 
 | #define CLK_SYNTH		0x00 | 
 | #define CLK_SYNTH_LO_SFT	0 | 
 | #define CLK_SYNTH_HI_SFT	8 | 
 | #define CLK_SYNTH_HOLD_SFT	16 | 
 | #define CLK_SYNTH_EN		BIT(24) | 
 | #define CLK_SYS_CYCLES_MAX	514 | 
 | #define CLK_SYS_CYCLES_MIN	2 | 
 |  | 
 | #define SDIF_DISABLE	0x04 | 
 |  | 
 | #define SDIF_STAT	0x08 | 
 | #define SDIF_BUSY	BIT(0) | 
 | #define SDIF_LOCK	BIT(1) | 
 |  | 
 | #define SDIF_W		0x0c | 
 | #define SDIF_PROG	BIT(31) | 
 | #define SDIF_WRN_W	BIT(27) | 
 | #define SDIF_WRN_R	0x00 | 
 | #define SDIF_ADDR_SFT	24 | 
 |  | 
 | #define SDIF_HALT	0x10 | 
 | #define SDIF_CTRL	0x14 | 
 | #define SDIF_SMPL_CTRL	0x20 | 
 |  | 
 | /* TS & PD Individual Macro Register */ | 
 | #define COM_REG_SIZE	0x40 | 
 |  | 
 | #define SDIF_DONE(n)	(COM_REG_SIZE + 0x14 + 0x40 * (n)) | 
 | #define SDIF_SMPL_DONE	BIT(0) | 
 |  | 
 | #define SDIF_DATA(n)	(COM_REG_SIZE + 0x18 + 0x40 * (n)) | 
 | #define SAMPLE_DATA_MSK	GENMASK(15, 0) | 
 |  | 
 | #define HILO_RESET(n)	(COM_REG_SIZE + 0x2c + 0x40 * (n)) | 
 |  | 
 | /* VM Individual Macro Register */ | 
 | #define VM_COM_REG_SIZE	0x200 | 
 | #define VM_SDIF_DONE(vm)	(VM_COM_REG_SIZE + 0x34 + 0x200 * (vm)) | 
 | #define VM_SDIF_DATA(vm, ch)	\ | 
 | 	(VM_COM_REG_SIZE + 0x40 + 0x200 * (vm) + 0x4 * (ch)) | 
 |  | 
 | /* SDA Slave Register */ | 
 | #define IP_CTRL			0x00 | 
 | #define IP_RST_REL		BIT(1) | 
 | #define IP_RUN_CONT		BIT(3) | 
 | #define IP_AUTO			BIT(8) | 
 | #define IP_VM_MODE		BIT(10) | 
 |  | 
 | #define IP_CFG			0x01 | 
 | #define CFG0_MODE_2		BIT(0) | 
 | #define CFG0_PARALLEL_OUT	0 | 
 | #define CFG0_12_BIT		0 | 
 | #define CFG1_VOL_MEAS_MODE	0 | 
 | #define CFG1_PARALLEL_OUT	0 | 
 | #define CFG1_14_BIT		0 | 
 |  | 
 | #define IP_DATA		0x03 | 
 |  | 
 | #define IP_POLL		0x04 | 
 | #define VM_CH_INIT	BIT(20) | 
 | #define VM_CH_REQ	BIT(21) | 
 |  | 
 | #define IP_TMR			0x05 | 
 | #define POWER_DELAY_CYCLE_256	0x100 | 
 | #define POWER_DELAY_CYCLE_64	0x40 | 
 |  | 
 | #define PVT_POLL_DELAY_US	20 | 
 | #define PVT_POLL_TIMEOUT_US	20000 | 
 | #define PVT_H_CONST		100000 | 
 | #define PVT_CAL5_CONST		2047 | 
 | #define PVT_G_CONST		40000 | 
 | #define PVT_CONV_BITS		10 | 
 | #define PVT_N_CONST		90 | 
 | #define PVT_R_CONST		245805 | 
 |  | 
 | struct pvt_device { | 
 | 	struct regmap		*c_map; | 
 | 	struct regmap		*t_map; | 
 | 	struct regmap		*p_map; | 
 | 	struct regmap		*v_map; | 
 | 	struct clk		*clk; | 
 | 	struct reset_control	*rst; | 
 | 	u32			t_num; | 
 | 	u32			p_num; | 
 | 	u32			v_num; | 
 | 	u32			c_num; | 
 | 	u32			ip_freq; | 
 | 	u8			*vm_idx; | 
 | }; | 
 |  | 
 | static umode_t pvt_is_visible(const void *data, enum hwmon_sensor_types type, | 
 | 			      u32 attr, int channel) | 
 | { | 
 | 	switch (type) { | 
 | 	case hwmon_temp: | 
 | 		if (attr == hwmon_temp_input) | 
 | 			return 0444; | 
 | 		break; | 
 | 	case hwmon_in: | 
 | 		if (attr == hwmon_in_input) | 
 | 			return 0444; | 
 | 		break; | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int pvt_read_temp(struct device *dev, u32 attr, int channel, long *val) | 
 | { | 
 | 	struct pvt_device *pvt = dev_get_drvdata(dev); | 
 | 	struct regmap *t_map = pvt->t_map; | 
 | 	u32 stat, nbs; | 
 | 	int ret; | 
 | 	u64 tmp; | 
 |  | 
 | 	switch (attr) { | 
 | 	case hwmon_temp_input: | 
 | 		ret = regmap_read_poll_timeout(t_map, SDIF_DONE(channel), | 
 | 					       stat, stat & SDIF_SMPL_DONE, | 
 | 					       PVT_POLL_DELAY_US, | 
 | 					       PVT_POLL_TIMEOUT_US); | 
 | 		if (ret) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_read(t_map, SDIF_DATA(channel), &nbs); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		nbs &= SAMPLE_DATA_MSK; | 
 |  | 
 | 		/* | 
 | 		 * Convert the register value to | 
 | 		 * degrees centigrade temperature | 
 | 		 */ | 
 | 		tmp = nbs * PVT_H_CONST; | 
 | 		do_div(tmp, PVT_CAL5_CONST); | 
 | 		*val = tmp - PVT_G_CONST - pvt->ip_freq; | 
 |  | 
 | 		return 0; | 
 | 	default: | 
 | 		return -EOPNOTSUPP; | 
 | 	} | 
 | } | 
 |  | 
 | static int pvt_read_in(struct device *dev, u32 attr, int channel, long *val) | 
 | { | 
 | 	struct pvt_device *pvt = dev_get_drvdata(dev); | 
 | 	struct regmap *v_map = pvt->v_map; | 
 | 	u8 vm_idx, ch_idx; | 
 | 	u32 n, stat; | 
 | 	int ret; | 
 |  | 
 | 	if (channel >= pvt->v_num * pvt->c_num) | 
 | 		return -EINVAL; | 
 |  | 
 | 	vm_idx = pvt->vm_idx[channel / pvt->c_num]; | 
 | 	ch_idx = channel % pvt->c_num; | 
 |  | 
 | 	switch (attr) { | 
 | 	case hwmon_in_input: | 
 | 		ret = regmap_read_poll_timeout(v_map, VM_SDIF_DONE(vm_idx), | 
 | 					       stat, stat & SDIF_SMPL_DONE, | 
 | 					       PVT_POLL_DELAY_US, | 
 | 					       PVT_POLL_TIMEOUT_US); | 
 | 		if (ret) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_read(v_map, VM_SDIF_DATA(vm_idx, ch_idx), &n); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		n &= SAMPLE_DATA_MSK; | 
 | 		/* | 
 | 		 * Convert the N bitstream count into voltage. | 
 | 		 * To support negative voltage calculation for 64bit machines | 
 | 		 * n must be cast to long, since n and *val differ both in | 
 | 		 * signedness and in size. | 
 | 		 * Division is used instead of right shift, because for signed | 
 | 		 * numbers, the sign bit is used to fill the vacated bit | 
 | 		 * positions, and if the number is negative, 1 is used. | 
 | 		 * BIT(x) may not be used instead of (1 << x) because it's | 
 | 		 * unsigned. | 
 | 		 */ | 
 | 		*val = (PVT_N_CONST * (long)n - PVT_R_CONST) / (1 << PVT_CONV_BITS); | 
 |  | 
 | 		return 0; | 
 | 	default: | 
 | 		return -EOPNOTSUPP; | 
 | 	} | 
 | } | 
 |  | 
 | static int pvt_read(struct device *dev, enum hwmon_sensor_types type, | 
 | 		    u32 attr, int channel, long *val) | 
 | { | 
 | 	switch (type) { | 
 | 	case hwmon_temp: | 
 | 		return pvt_read_temp(dev, attr, channel, val); | 
 | 	case hwmon_in: | 
 | 		return pvt_read_in(dev, attr, channel, val); | 
 | 	default: | 
 | 		return -EOPNOTSUPP; | 
 | 	} | 
 | } | 
 |  | 
 | static const u32 pvt_chip_config[] = { | 
 | 	HWMON_C_REGISTER_TZ, | 
 | 	0 | 
 | }; | 
 |  | 
 | static const struct hwmon_channel_info pvt_chip = { | 
 | 	.type = hwmon_chip, | 
 | 	.config = pvt_chip_config, | 
 | }; | 
 |  | 
 | static struct hwmon_channel_info pvt_temp = { | 
 | 	.type = hwmon_temp, | 
 | }; | 
 |  | 
 | static struct hwmon_channel_info pvt_in = { | 
 | 	.type = hwmon_in, | 
 | }; | 
 |  | 
 | static const struct hwmon_ops pvt_hwmon_ops = { | 
 | 	.is_visible = pvt_is_visible, | 
 | 	.read = pvt_read, | 
 | }; | 
 |  | 
 | static struct hwmon_chip_info pvt_chip_info = { | 
 | 	.ops = &pvt_hwmon_ops, | 
 | }; | 
 |  | 
 | static int pvt_init(struct pvt_device *pvt) | 
 | { | 
 | 	u16 sys_freq, key, middle, low = 4, high = 8; | 
 | 	struct regmap *t_map = pvt->t_map; | 
 | 	struct regmap *p_map = pvt->p_map; | 
 | 	struct regmap *v_map = pvt->v_map; | 
 | 	u32 t_num = pvt->t_num; | 
 | 	u32 p_num = pvt->p_num; | 
 | 	u32 v_num = pvt->v_num; | 
 | 	u32 clk_synth, val; | 
 | 	int ret; | 
 |  | 
 | 	sys_freq = clk_get_rate(pvt->clk) / HZ_PER_MHZ; | 
 | 	while (high >= low) { | 
 | 		middle = (low + high + 1) / 2; | 
 | 		key = DIV_ROUND_CLOSEST(sys_freq, middle); | 
 | 		if (key > CLK_SYS_CYCLES_MAX) { | 
 | 			low = middle + 1; | 
 | 			continue; | 
 | 		} else if (key < CLK_SYS_CYCLES_MIN) { | 
 | 			high = middle - 1; | 
 | 			continue; | 
 | 		} else { | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * The system supports 'clk_sys' to 'clk_ip' frequency ratios | 
 | 	 * from 2:1 to 512:1 | 
 | 	 */ | 
 | 	key = clamp_val(key, CLK_SYS_CYCLES_MIN, CLK_SYS_CYCLES_MAX) - 2; | 
 |  | 
 | 	clk_synth = ((key + 1) >> 1) << CLK_SYNTH_LO_SFT | | 
 | 		    (key >> 1) << CLK_SYNTH_HI_SFT | | 
 | 		    (key >> 1) << CLK_SYNTH_HOLD_SFT | CLK_SYNTH_EN; | 
 |  | 
 | 	pvt->ip_freq = sys_freq * 100 / (key + 2); | 
 |  | 
 | 	if (t_num) { | 
 | 		ret = regmap_write(t_map, SDIF_SMPL_CTRL, 0x0); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_write(t_map, SDIF_HALT, 0x0); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_write(t_map, CLK_SYNTH, clk_synth); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_write(t_map, SDIF_DISABLE, 0x0); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_read_poll_timeout(t_map, SDIF_STAT, | 
 | 					       val, !(val & SDIF_BUSY), | 
 | 					       PVT_POLL_DELAY_US, | 
 | 					       PVT_POLL_TIMEOUT_US); | 
 | 		if (ret) | 
 | 			return ret; | 
 |  | 
 | 		val = CFG0_MODE_2 | CFG0_PARALLEL_OUT | CFG0_12_BIT | | 
 | 		      IP_CFG << SDIF_ADDR_SFT | SDIF_WRN_W | SDIF_PROG; | 
 | 		ret = regmap_write(t_map, SDIF_W, val); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_read_poll_timeout(t_map, SDIF_STAT, | 
 | 					       val, !(val & SDIF_BUSY), | 
 | 					       PVT_POLL_DELAY_US, | 
 | 					       PVT_POLL_TIMEOUT_US); | 
 | 		if (ret) | 
 | 			return ret; | 
 |  | 
 | 		val = POWER_DELAY_CYCLE_256 | IP_TMR << SDIF_ADDR_SFT | | 
 | 			      SDIF_WRN_W | SDIF_PROG; | 
 | 		ret = regmap_write(t_map, SDIF_W, val); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_read_poll_timeout(t_map, SDIF_STAT, | 
 | 					       val, !(val & SDIF_BUSY), | 
 | 					       PVT_POLL_DELAY_US, | 
 | 					       PVT_POLL_TIMEOUT_US); | 
 | 		if (ret) | 
 | 			return ret; | 
 |  | 
 | 		val = IP_RST_REL | IP_RUN_CONT | IP_AUTO | | 
 | 		      IP_CTRL << SDIF_ADDR_SFT | | 
 | 		      SDIF_WRN_W | SDIF_PROG; | 
 | 		ret = regmap_write(t_map, SDIF_W, val); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 | 	} | 
 |  | 
 | 	if (p_num) { | 
 | 		ret = regmap_write(p_map, SDIF_HALT, 0x0); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_write(p_map, SDIF_DISABLE, BIT(p_num) - 1); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_write(p_map, CLK_SYNTH, clk_synth); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 | 	} | 
 |  | 
 | 	if (v_num) { | 
 | 		ret = regmap_write(v_map, SDIF_SMPL_CTRL, 0x0); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_write(v_map, SDIF_HALT, 0x0); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_write(v_map, CLK_SYNTH, clk_synth); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_write(v_map, SDIF_DISABLE, 0x0); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_read_poll_timeout(v_map, SDIF_STAT, | 
 | 					       val, !(val & SDIF_BUSY), | 
 | 					       PVT_POLL_DELAY_US, | 
 | 					       PVT_POLL_TIMEOUT_US); | 
 | 		if (ret) | 
 | 			return ret; | 
 |  | 
 | 		val = (BIT(pvt->c_num) - 1) | VM_CH_INIT | | 
 | 		      IP_POLL << SDIF_ADDR_SFT | SDIF_WRN_W | SDIF_PROG; | 
 | 		ret = regmap_write(v_map, SDIF_W, val); | 
 | 		if (ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_read_poll_timeout(v_map, SDIF_STAT, | 
 | 					       val, !(val & SDIF_BUSY), | 
 | 					       PVT_POLL_DELAY_US, | 
 | 					       PVT_POLL_TIMEOUT_US); | 
 | 		if (ret) | 
 | 			return ret; | 
 |  | 
 | 		val = CFG1_VOL_MEAS_MODE | CFG1_PARALLEL_OUT | | 
 | 		      CFG1_14_BIT | IP_CFG << SDIF_ADDR_SFT | | 
 | 		      SDIF_WRN_W | SDIF_PROG; | 
 | 		ret = regmap_write(v_map, SDIF_W, val); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_read_poll_timeout(v_map, SDIF_STAT, | 
 | 					       val, !(val & SDIF_BUSY), | 
 | 					       PVT_POLL_DELAY_US, | 
 | 					       PVT_POLL_TIMEOUT_US); | 
 | 		if (ret) | 
 | 			return ret; | 
 |  | 
 | 		val = POWER_DELAY_CYCLE_64 | IP_TMR << SDIF_ADDR_SFT | | 
 | 		      SDIF_WRN_W | SDIF_PROG; | 
 | 		ret = regmap_write(v_map, SDIF_W, val); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 |  | 
 | 		ret = regmap_read_poll_timeout(v_map, SDIF_STAT, | 
 | 					       val, !(val & SDIF_BUSY), | 
 | 					       PVT_POLL_DELAY_US, | 
 | 					       PVT_POLL_TIMEOUT_US); | 
 | 		if (ret) | 
 | 			return ret; | 
 |  | 
 | 		val = IP_RST_REL | IP_RUN_CONT | IP_AUTO | IP_VM_MODE | | 
 | 		      IP_CTRL << SDIF_ADDR_SFT | | 
 | 		      SDIF_WRN_W | SDIF_PROG; | 
 | 		ret = regmap_write(v_map, SDIF_W, val); | 
 | 		if(ret < 0) | 
 | 			return ret; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static struct regmap_config pvt_regmap_config = { | 
 | 	.reg_bits = 32, | 
 | 	.reg_stride = 4, | 
 | 	.val_bits = 32, | 
 | }; | 
 |  | 
 | static int pvt_get_regmap(struct platform_device *pdev, char *reg_name, | 
 | 			  struct pvt_device *pvt) | 
 | { | 
 | 	struct device *dev = &pdev->dev; | 
 | 	struct regmap **reg_map; | 
 | 	void __iomem *io_base; | 
 |  | 
 | 	if (!strcmp(reg_name, "common")) | 
 | 		reg_map = &pvt->c_map; | 
 | 	else if (!strcmp(reg_name, "ts")) | 
 | 		reg_map = &pvt->t_map; | 
 | 	else if (!strcmp(reg_name, "pd")) | 
 | 		reg_map = &pvt->p_map; | 
 | 	else if (!strcmp(reg_name, "vm")) | 
 | 		reg_map = &pvt->v_map; | 
 | 	else | 
 | 		return -EINVAL; | 
 |  | 
 | 	io_base = devm_platform_ioremap_resource_byname(pdev, reg_name); | 
 | 	if (IS_ERR(io_base)) | 
 | 		return PTR_ERR(io_base); | 
 |  | 
 | 	pvt_regmap_config.name = reg_name; | 
 | 	*reg_map = devm_regmap_init_mmio(dev, io_base, &pvt_regmap_config); | 
 | 	if (IS_ERR(*reg_map)) { | 
 | 		dev_err(dev, "failed to init register map\n"); | 
 | 		return PTR_ERR(*reg_map); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void pvt_clk_disable(void *data) | 
 | { | 
 | 	struct pvt_device *pvt = data; | 
 |  | 
 | 	clk_disable_unprepare(pvt->clk); | 
 | } | 
 |  | 
 | static int pvt_clk_enable(struct device *dev, struct pvt_device *pvt) | 
 | { | 
 | 	int ret; | 
 |  | 
 | 	ret = clk_prepare_enable(pvt->clk); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	return devm_add_action_or_reset(dev, pvt_clk_disable, pvt); | 
 | } | 
 |  | 
 | static void pvt_reset_control_assert(void *data) | 
 | { | 
 | 	struct pvt_device *pvt = data; | 
 |  | 
 | 	reset_control_assert(pvt->rst); | 
 | } | 
 |  | 
 | static int pvt_reset_control_deassert(struct device *dev, struct pvt_device *pvt) | 
 | { | 
 | 	int ret; | 
 |  | 
 | 	ret = reset_control_deassert(pvt->rst); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	return devm_add_action_or_reset(dev, pvt_reset_control_assert, pvt); | 
 | } | 
 |  | 
 | static int mr75203_probe(struct platform_device *pdev) | 
 | { | 
 | 	u32 ts_num, vm_num, pd_num, ch_num, val, index, i; | 
 | 	const struct hwmon_channel_info **pvt_info; | 
 | 	struct device *dev = &pdev->dev; | 
 | 	u32 *temp_config, *in_config; | 
 | 	struct device *hwmon_dev; | 
 | 	struct pvt_device *pvt; | 
 | 	int ret; | 
 |  | 
 | 	pvt = devm_kzalloc(dev, sizeof(*pvt), GFP_KERNEL); | 
 | 	if (!pvt) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	ret = pvt_get_regmap(pdev, "common", pvt); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	pvt->clk = devm_clk_get(dev, NULL); | 
 | 	if (IS_ERR(pvt->clk)) | 
 | 		return dev_err_probe(dev, PTR_ERR(pvt->clk), "failed to get clock\n"); | 
 |  | 
 | 	ret = pvt_clk_enable(dev, pvt); | 
 | 	if (ret) { | 
 | 		dev_err(dev, "failed to enable clock\n"); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	pvt->rst = devm_reset_control_get_exclusive(dev, NULL); | 
 | 	if (IS_ERR(pvt->rst)) | 
 | 		return dev_err_probe(dev, PTR_ERR(pvt->rst), | 
 | 				     "failed to get reset control\n"); | 
 |  | 
 | 	ret = pvt_reset_control_deassert(dev, pvt); | 
 | 	if (ret) | 
 | 		return dev_err_probe(dev, ret, "cannot deassert reset control\n"); | 
 |  | 
 | 	ret = regmap_read(pvt->c_map, PVT_IP_CONFIG, &val); | 
 | 	if(ret < 0) | 
 | 		return ret; | 
 |  | 
 | 	ts_num = (val & TS_NUM_MSK) >> TS_NUM_SFT; | 
 | 	pd_num = (val & PD_NUM_MSK) >> PD_NUM_SFT; | 
 | 	vm_num = (val & VM_NUM_MSK) >> VM_NUM_SFT; | 
 | 	ch_num = (val & CH_NUM_MSK) >> CH_NUM_SFT; | 
 | 	pvt->t_num = ts_num; | 
 | 	pvt->p_num = pd_num; | 
 | 	pvt->v_num = vm_num; | 
 | 	pvt->c_num = ch_num; | 
 | 	val = 0; | 
 | 	if (ts_num) | 
 | 		val++; | 
 | 	if (vm_num) | 
 | 		val++; | 
 | 	if (!val) | 
 | 		return -ENODEV; | 
 |  | 
 | 	pvt_info = devm_kcalloc(dev, val + 2, sizeof(*pvt_info), GFP_KERNEL); | 
 | 	if (!pvt_info) | 
 | 		return -ENOMEM; | 
 | 	pvt_info[0] = &pvt_chip; | 
 | 	index = 1; | 
 |  | 
 | 	if (ts_num) { | 
 | 		ret = pvt_get_regmap(pdev, "ts", pvt); | 
 | 		if (ret) | 
 | 			return ret; | 
 |  | 
 | 		temp_config = devm_kcalloc(dev, ts_num + 1, | 
 | 					   sizeof(*temp_config), GFP_KERNEL); | 
 | 		if (!temp_config) | 
 | 			return -ENOMEM; | 
 |  | 
 | 		memset32(temp_config, HWMON_T_INPUT, ts_num); | 
 | 		pvt_temp.config = temp_config; | 
 | 		pvt_info[index++] = &pvt_temp; | 
 | 	} | 
 |  | 
 | 	if (pd_num) { | 
 | 		ret = pvt_get_regmap(pdev, "pd", pvt); | 
 | 		if (ret) | 
 | 			return ret; | 
 | 	} | 
 |  | 
 | 	if (vm_num) { | 
 | 		u32 total_ch; | 
 |  | 
 | 		ret = pvt_get_regmap(pdev, "vm", pvt); | 
 | 		if (ret) | 
 | 			return ret; | 
 |  | 
 | 		pvt->vm_idx = devm_kcalloc(dev, vm_num, sizeof(*pvt->vm_idx), | 
 | 					   GFP_KERNEL); | 
 | 		if (!pvt->vm_idx) | 
 | 			return -ENOMEM; | 
 |  | 
 | 		ret = device_property_read_u8_array(dev, "intel,vm-map", | 
 | 						    pvt->vm_idx, vm_num); | 
 | 		if (ret) { | 
 | 			/* | 
 | 			 * Incase intel,vm-map property is not defined, we | 
 | 			 * assume incremental channel numbers. | 
 | 			 */ | 
 | 			for (i = 0; i < vm_num; i++) | 
 | 				pvt->vm_idx[i] = i; | 
 | 		} else { | 
 | 			for (i = 0; i < vm_num; i++) | 
 | 				if (pvt->vm_idx[i] >= vm_num || | 
 | 				    pvt->vm_idx[i] == 0xff) { | 
 | 					pvt->v_num = i; | 
 | 					vm_num = i; | 
 | 					break; | 
 | 				} | 
 | 		} | 
 |  | 
 | 		total_ch = ch_num * vm_num; | 
 | 		in_config = devm_kcalloc(dev, total_ch + 1, | 
 | 					 sizeof(*in_config), GFP_KERNEL); | 
 | 		if (!in_config) | 
 | 			return -ENOMEM; | 
 |  | 
 | 		memset32(in_config, HWMON_I_INPUT, total_ch); | 
 | 		in_config[total_ch] = 0; | 
 | 		pvt_in.config = in_config; | 
 |  | 
 | 		pvt_info[index++] = &pvt_in; | 
 | 	} | 
 |  | 
 | 	ret = pvt_init(pvt); | 
 | 	if (ret) { | 
 | 		dev_err(dev, "failed to init pvt: %d\n", ret); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	pvt_chip_info.info = pvt_info; | 
 | 	hwmon_dev = devm_hwmon_device_register_with_info(dev, "pvt", | 
 | 							 pvt, | 
 | 							 &pvt_chip_info, | 
 | 							 NULL); | 
 |  | 
 | 	return PTR_ERR_OR_ZERO(hwmon_dev); | 
 | } | 
 |  | 
 | static const struct of_device_id moortec_pvt_of_match[] = { | 
 | 	{ .compatible = "moortec,mr75203" }, | 
 | 	{ } | 
 | }; | 
 | MODULE_DEVICE_TABLE(of, moortec_pvt_of_match); | 
 |  | 
 | static struct platform_driver moortec_pvt_driver = { | 
 | 	.driver = { | 
 | 		.name = "moortec-pvt", | 
 | 		.of_match_table = moortec_pvt_of_match, | 
 | 	}, | 
 | 	.probe = mr75203_probe, | 
 | }; | 
 | module_platform_driver(moortec_pvt_driver); | 
 |  | 
 | MODULE_LICENSE("GPL v2"); |