static int nct7904_probe()

in nct7904.c [1012:1161]


static int nct7904_probe(struct i2c_client *client)
{
	struct nct7904_data *data;
	struct device *hwmon_dev;
	struct device *dev = &client->dev;
	int ret, i;
	u32 mask;
	u8 val, bit;

	data = devm_kzalloc(dev, sizeof(struct nct7904_data), GFP_KERNEL);
	if (!data)
		return -ENOMEM;

	data->client = client;
	mutex_init(&data->bank_lock);
	data->bank_sel = -1;

	/* Setup sensor groups. */
	/* FANIN attributes */
	ret = nct7904_read_reg16(data, BANK_0, FANIN_CTRL0_REG);
	if (ret < 0)
		return ret;
	data->fanin_mask = (ret >> 8) | ((ret & 0xff) << 8);

	/*
	 * VSEN attributes
	 *
	 * Note: voltage sensors overlap with external temperature
	 * sensors. So, if we ever decide to support the latter
	 * we will have to adjust 'vsen_mask' accordingly.
	 */
	mask = 0;
	ret = nct7904_read_reg16(data, BANK_0, VT_ADC_CTRL0_REG);
	if (ret >= 0)
		mask = (ret >> 8) | ((ret & 0xff) << 8);
	ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG);
	if (ret >= 0)
		mask |= (ret << 16);
	data->vsen_mask = mask;

	/* CPU_TEMP attributes */
	ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL0_REG);
	if (ret < 0)
		return ret;

	if ((ret & 0x6) == 0x6)
		data->tcpu_mask |= 1; /* TR1 */
	if ((ret & 0x18) == 0x18)
		data->tcpu_mask |= 2; /* TR2 */
	if ((ret & 0x20) == 0x20)
		data->tcpu_mask |= 4; /* TR3 */
	if ((ret & 0x80) == 0x80)
		data->tcpu_mask |= 8; /* TR4 */

	/* LTD */
	ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG);
	if (ret < 0)
		return ret;
	if ((ret & 0x02) == 0x02)
		data->tcpu_mask |= 0x10;

	/* Multi-Function detecting for Volt and TR/TD */
	ret = nct7904_read_reg(data, BANK_0, VT_ADC_MD_REG);
	if (ret < 0)
		return ret;

	data->temp_mode = 0;
	for (i = 0; i < 4; i++) {
		val = (ret >> (i * 2)) & 0x03;
		bit = (1 << i);
		if (val == VOLT_MONITOR_MODE) {
			data->tcpu_mask &= ~bit;
		} else if (val == THERMAL_DIODE_MODE && i < 2) {
			data->temp_mode |= bit;
			data->vsen_mask &= ~(0x06 << (i * 2));
		} else if (val == THERMISTOR_MODE) {
			data->vsen_mask &= ~(0x02 << (i * 2));
		} else {
			/* Reserved */
			data->tcpu_mask &= ~bit;
			data->vsen_mask &= ~(0x06 << (i * 2));
		}
	}

	/* PECI */
	ret = nct7904_read_reg(data, BANK_2, PFE_REG);
	if (ret < 0)
		return ret;
	if (ret & 0x80) {
		data->enable_dts = 1; /* Enable DTS & PECI */
	} else {
		ret = nct7904_read_reg(data, BANK_2, TSI_CTRL_REG);
		if (ret < 0)
			return ret;
		if (ret & 0x80)
			data->enable_dts = 0x3; /* Enable DTS & TSI */
	}

	/* Check DTS enable status */
	if (data->enable_dts) {
		ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL0_REG);
		if (ret < 0)
			return ret;
		data->has_dts = ret & 0xF;
		if (data->enable_dts & ENABLE_TSI) {
			ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL1_REG);
			if (ret < 0)
				return ret;
			data->has_dts |= (ret & 0xF) << 4;
		}
	}

	for (i = 0; i < FANCTL_MAX; i++) {
		ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + i);
		if (ret < 0)
			return ret;
		data->fan_mode[i] = ret;
	}

	/* Read all of SMI status register to clear alarms */
	for (i = 0; i < SMI_STS_MAX; i++) {
		ret = nct7904_read_reg(data, BANK_0, SMI_STS1_REG + i);
		if (ret < 0)
			return ret;
	}

	hwmon_dev =
		devm_hwmon_device_register_with_info(dev, client->name, data,
						     &nct7904_chip_info, NULL);
	ret = PTR_ERR_OR_ZERO(hwmon_dev);
	if (ret)
		return ret;

	/* Watchdog initialization */
	data->wdt.ops = &nct7904_wdt_ops;
	data->wdt.info = &nct7904_wdt_info;

	data->wdt.timeout = WATCHDOG_TIMEOUT * 60; /* Set default timeout */
	data->wdt.min_timeout = MIN_TIMEOUT;
	data->wdt.max_timeout = MAX_TIMEOUT;
	data->wdt.parent = &client->dev;

	watchdog_init_timeout(&data->wdt, timeout * 60, &client->dev);
	watchdog_set_nowayout(&data->wdt, nowayout);
	watchdog_set_drvdata(&data->wdt, data);

	watchdog_stop_on_unregister(&data->wdt);

	return devm_watchdog_register_device(dev, &data->wdt);
}