162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * pc87360.c - Part of lm_sensors, Linux kernel modules 462306a36Sopenharmony_ci * for hardware monitoring 562306a36Sopenharmony_ci * Copyright (C) 2004, 2007 Jean Delvare <jdelvare@suse.de> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copied from smsc47m1.c: 862306a36Sopenharmony_ci * Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Supports the following chips: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Chip #vin #fan #pwm #temp devid 1362306a36Sopenharmony_ci * PC87360 - 2 2 - 0xE1 1462306a36Sopenharmony_ci * PC87363 - 2 2 - 0xE8 1562306a36Sopenharmony_ci * PC87364 - 3 3 - 0xE4 1662306a36Sopenharmony_ci * PC87365 11 3 3 2 0xE5 1762306a36Sopenharmony_ci * PC87366 11 3 3 3-4 0xE9 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * This driver assumes that no more than one chip is present, and one of 2062306a36Sopenharmony_ci * the standard Super-I/O addresses is used (0x2E/0x2F or 0x4E/0x4F). 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/module.h> 2662306a36Sopenharmony_ci#include <linux/init.h> 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci#include <linux/jiffies.h> 2962306a36Sopenharmony_ci#include <linux/platform_device.h> 3062306a36Sopenharmony_ci#include <linux/hwmon.h> 3162306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h> 3262306a36Sopenharmony_ci#include <linux/hwmon-vid.h> 3362306a36Sopenharmony_ci#include <linux/err.h> 3462306a36Sopenharmony_ci#include <linux/mutex.h> 3562306a36Sopenharmony_ci#include <linux/acpi.h> 3662306a36Sopenharmony_ci#include <linux/io.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define DRIVER_NAME "pc87360" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* (temp & vin) channel conversion status register flags (pdf sec.11.5.12) */ 4162306a36Sopenharmony_ci#define CHAN_CNVRTD 0x80 /* new data ready */ 4262306a36Sopenharmony_ci#define CHAN_ENA 0x01 /* enabled channel (temp or vin) */ 4362306a36Sopenharmony_ci#define CHAN_ALM_ENA 0x10 /* propagate to alarms-reg ?? (chk val!) */ 4462306a36Sopenharmony_ci#define CHAN_READY (CHAN_ENA|CHAN_CNVRTD) /* sample ready mask */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define TEMP_OTS_OE 0x20 /* OTS Output Enable */ 4762306a36Sopenharmony_ci#define VIN_RW1C_MASK (CHAN_READY|CHAN_ALM_MAX|CHAN_ALM_MIN) /* 0x87 */ 4862306a36Sopenharmony_ci#define TEMP_RW1C_MASK (VIN_RW1C_MASK|TEMP_ALM_CRIT|TEMP_FAULT) /* 0xCF */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic u8 devid; 5162306a36Sopenharmony_cistatic struct platform_device *pdev; 5262306a36Sopenharmony_cistatic unsigned short extra_isa[3]; 5362306a36Sopenharmony_cistatic u8 confreg[4]; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int init = 1; 5662306a36Sopenharmony_cimodule_param(init, int, 0); 5762306a36Sopenharmony_ciMODULE_PARM_DESC(init, 5862306a36Sopenharmony_ci"Chip initialization level:\n" 5962306a36Sopenharmony_ci" 0: None\n" 6062306a36Sopenharmony_ci"*1: Forcibly enable internal voltage and temperature channels, except in9\n" 6162306a36Sopenharmony_ci" 2: Forcibly enable all voltage and temperature channels, except in9\n" 6262306a36Sopenharmony_ci" 3: Forcibly enable all voltage and temperature channels, including in9"); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic unsigned short force_id; 6562306a36Sopenharmony_cimodule_param(force_id, ushort, 0); 6662306a36Sopenharmony_ciMODULE_PARM_DESC(force_id, "Override the detected device ID"); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* 6962306a36Sopenharmony_ci * Super-I/O registers and operations 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define DEV 0x07 /* Register: Logical device select */ 7362306a36Sopenharmony_ci#define DEVID 0x20 /* Register: Device ID */ 7462306a36Sopenharmony_ci#define ACT 0x30 /* Register: Device activation */ 7562306a36Sopenharmony_ci#define BASE 0x60 /* Register: Base address */ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define FSCM 0x09 /* Logical device: fans */ 7862306a36Sopenharmony_ci#define VLM 0x0d /* Logical device: voltages */ 7962306a36Sopenharmony_ci#define TMS 0x0e /* Logical device: temperatures */ 8062306a36Sopenharmony_ci#define LDNI_MAX 3 8162306a36Sopenharmony_cistatic const u8 logdev[LDNI_MAX] = { FSCM, VLM, TMS }; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define LD_FAN 0 8462306a36Sopenharmony_ci#define LD_IN 1 8562306a36Sopenharmony_ci#define LD_TEMP 2 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic inline void superio_outb(int sioaddr, int reg, int val) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci outb(reg, sioaddr); 9062306a36Sopenharmony_ci outb(val, sioaddr + 1); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic inline int superio_inb(int sioaddr, int reg) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci outb(reg, sioaddr); 9662306a36Sopenharmony_ci return inb(sioaddr + 1); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic inline void superio_exit(int sioaddr) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci outb(0x02, sioaddr); 10262306a36Sopenharmony_ci outb(0x02, sioaddr + 1); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* 10662306a36Sopenharmony_ci * Logical devices 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define PC87360_EXTENT 0x10 11062306a36Sopenharmony_ci#define PC87365_REG_BANK 0x09 11162306a36Sopenharmony_ci#define NO_BANK 0xff 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* 11462306a36Sopenharmony_ci * Fan registers and conversions 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* nr has to be 0 or 1 (PC87360/87363) or 2 (PC87364/87365/87366) */ 11862306a36Sopenharmony_ci#define PC87360_REG_PRESCALE(nr) (0x00 + 2 * (nr)) 11962306a36Sopenharmony_ci#define PC87360_REG_PWM(nr) (0x01 + 2 * (nr)) 12062306a36Sopenharmony_ci#define PC87360_REG_FAN_MIN(nr) (0x06 + 3 * (nr)) 12162306a36Sopenharmony_ci#define PC87360_REG_FAN(nr) (0x07 + 3 * (nr)) 12262306a36Sopenharmony_ci#define PC87360_REG_FAN_STATUS(nr) (0x08 + 3 * (nr)) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : \ 12562306a36Sopenharmony_ci 480000 / ((val) * (div))) 12662306a36Sopenharmony_ci#define FAN_TO_REG(val, div) ((val) <= 100 ? 0 : \ 12762306a36Sopenharmony_ci 480000 / ((val) * (div))) 12862306a36Sopenharmony_ci#define FAN_DIV_FROM_REG(val) (1 << (((val) >> 5) & 0x03)) 12962306a36Sopenharmony_ci#define FAN_STATUS_FROM_REG(val) ((val) & 0x07) 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#define FAN_CONFIG_MONITOR(val, nr) (((val) >> (2 + (nr) * 3)) & 1) 13262306a36Sopenharmony_ci#define FAN_CONFIG_CONTROL(val, nr) (((val) >> (3 + (nr) * 3)) & 1) 13362306a36Sopenharmony_ci#define FAN_CONFIG_INVERT(val, nr) (((val) >> (4 + (nr) * 3)) & 1) 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci#define PWM_FROM_REG(val, inv) ((inv) ? 255 - (val) : (val)) 13662306a36Sopenharmony_cistatic inline u8 PWM_TO_REG(int val, int inv) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci if (inv) 13962306a36Sopenharmony_ci val = 255 - val; 14062306a36Sopenharmony_ci if (val < 0) 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci if (val > 255) 14362306a36Sopenharmony_ci return 255; 14462306a36Sopenharmony_ci return val; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* 14862306a36Sopenharmony_ci * Voltage registers and conversions 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#define PC87365_REG_IN_CONVRATE 0x07 15262306a36Sopenharmony_ci#define PC87365_REG_IN_CONFIG 0x08 15362306a36Sopenharmony_ci#define PC87365_REG_IN 0x0B 15462306a36Sopenharmony_ci#define PC87365_REG_IN_MIN 0x0D 15562306a36Sopenharmony_ci#define PC87365_REG_IN_MAX 0x0C 15662306a36Sopenharmony_ci#define PC87365_REG_IN_STATUS 0x0A 15762306a36Sopenharmony_ci#define PC87365_REG_IN_ALARMS1 0x00 15862306a36Sopenharmony_ci#define PC87365_REG_IN_ALARMS2 0x01 15962306a36Sopenharmony_ci#define PC87365_REG_VID 0x06 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci#define IN_FROM_REG(val, ref) (((val) * (ref) + 128) / 256) 16262306a36Sopenharmony_ci#define IN_TO_REG(val, ref) ((val) < 0 ? 0 : \ 16362306a36Sopenharmony_ci (val) * 256 >= (ref) * 255 ? 255 : \ 16462306a36Sopenharmony_ci ((val) * 256 + (ref) / 2) / (ref)) 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* 16762306a36Sopenharmony_ci * Temperature registers and conversions 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci#define PC87365_REG_TEMP_CONFIG 0x08 17162306a36Sopenharmony_ci#define PC87365_REG_TEMP 0x0B 17262306a36Sopenharmony_ci#define PC87365_REG_TEMP_MIN 0x0D 17362306a36Sopenharmony_ci#define PC87365_REG_TEMP_MAX 0x0C 17462306a36Sopenharmony_ci#define PC87365_REG_TEMP_CRIT 0x0E 17562306a36Sopenharmony_ci#define PC87365_REG_TEMP_STATUS 0x0A 17662306a36Sopenharmony_ci#define PC87365_REG_TEMP_ALARMS 0x00 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci#define TEMP_FROM_REG(val) ((val) * 1000) 17962306a36Sopenharmony_ci#define TEMP_TO_REG(val) ((val) < -55000 ? -55 : \ 18062306a36Sopenharmony_ci (val) > 127000 ? 127 : \ 18162306a36Sopenharmony_ci (val) < 0 ? ((val) - 500) / 1000 : \ 18262306a36Sopenharmony_ci ((val) + 500) / 1000) 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci/* 18562306a36Sopenharmony_ci * Device data 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistruct pc87360_data { 18962306a36Sopenharmony_ci const char *name; 19062306a36Sopenharmony_ci struct device *hwmon_dev; 19162306a36Sopenharmony_ci struct mutex lock; 19262306a36Sopenharmony_ci struct mutex update_lock; 19362306a36Sopenharmony_ci bool valid; /* true if following fields are valid */ 19462306a36Sopenharmony_ci unsigned long last_updated; /* In jiffies */ 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci int address[3]; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci u8 fannr, innr, tempnr; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci u8 fan[3]; /* Register value */ 20162306a36Sopenharmony_ci u8 fan_min[3]; /* Register value */ 20262306a36Sopenharmony_ci u8 fan_status[3]; /* Register value */ 20362306a36Sopenharmony_ci u8 pwm[3]; /* Register value */ 20462306a36Sopenharmony_ci u16 fan_conf; /* Configuration register values, combined */ 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci u16 in_vref; /* 1 mV/bit */ 20762306a36Sopenharmony_ci u8 in[14]; /* Register value */ 20862306a36Sopenharmony_ci u8 in_min[14]; /* Register value */ 20962306a36Sopenharmony_ci u8 in_max[14]; /* Register value */ 21062306a36Sopenharmony_ci u8 in_crit[3]; /* Register value */ 21162306a36Sopenharmony_ci u8 in_status[14]; /* Register value */ 21262306a36Sopenharmony_ci u16 in_alarms; /* Register values, combined, masked */ 21362306a36Sopenharmony_ci u8 vid_conf; /* Configuration register value */ 21462306a36Sopenharmony_ci u8 vrm; 21562306a36Sopenharmony_ci u8 vid; /* Register value */ 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci s8 temp[3]; /* Register value */ 21862306a36Sopenharmony_ci s8 temp_min[3]; /* Register value */ 21962306a36Sopenharmony_ci s8 temp_max[3]; /* Register value */ 22062306a36Sopenharmony_ci s8 temp_crit[3]; /* Register value */ 22162306a36Sopenharmony_ci u8 temp_status[3]; /* Register value */ 22262306a36Sopenharmony_ci u8 temp_alarms; /* Register value, masked */ 22362306a36Sopenharmony_ci}; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* 22662306a36Sopenharmony_ci * ldi is the logical device index 22762306a36Sopenharmony_ci * bank is for voltages and temperatures only 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_cistatic int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, 23062306a36Sopenharmony_ci u8 reg) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci int res; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci mutex_lock(&(data->lock)); 23562306a36Sopenharmony_ci if (bank != NO_BANK) 23662306a36Sopenharmony_ci outb_p(bank, data->address[ldi] + PC87365_REG_BANK); 23762306a36Sopenharmony_ci res = inb_p(data->address[ldi] + reg); 23862306a36Sopenharmony_ci mutex_unlock(&(data->lock)); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return res; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, 24462306a36Sopenharmony_ci u8 reg, u8 value) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci mutex_lock(&(data->lock)); 24762306a36Sopenharmony_ci if (bank != NO_BANK) 24862306a36Sopenharmony_ci outb_p(bank, data->address[ldi] + PC87365_REG_BANK); 24962306a36Sopenharmony_ci outb_p(value, data->address[ldi] + reg); 25062306a36Sopenharmony_ci mutex_unlock(&(data->lock)); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void pc87360_autodiv(struct device *dev, int nr) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 25662306a36Sopenharmony_ci u8 old_min = data->fan_min[nr]; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Increase clock divider if needed and possible */ 25962306a36Sopenharmony_ci if ((data->fan_status[nr] & 0x04) /* overflow flag */ 26062306a36Sopenharmony_ci || (data->fan[nr] >= 224)) { /* next to overflow */ 26162306a36Sopenharmony_ci if ((data->fan_status[nr] & 0x60) != 0x60) { 26262306a36Sopenharmony_ci data->fan_status[nr] += 0x20; 26362306a36Sopenharmony_ci data->fan_min[nr] >>= 1; 26462306a36Sopenharmony_ci data->fan[nr] >>= 1; 26562306a36Sopenharmony_ci dev_dbg(dev, 26662306a36Sopenharmony_ci "Increasing clock divider to %d for fan %d\n", 26762306a36Sopenharmony_ci FAN_DIV_FROM_REG(data->fan_status[nr]), nr + 1); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci } else { 27062306a36Sopenharmony_ci /* Decrease clock divider if possible */ 27162306a36Sopenharmony_ci while (!(data->fan_min[nr] & 0x80) /* min "nails" divider */ 27262306a36Sopenharmony_ci && data->fan[nr] < 85 /* bad accuracy */ 27362306a36Sopenharmony_ci && (data->fan_status[nr] & 0x60) != 0x00) { 27462306a36Sopenharmony_ci data->fan_status[nr] -= 0x20; 27562306a36Sopenharmony_ci data->fan_min[nr] <<= 1; 27662306a36Sopenharmony_ci data->fan[nr] <<= 1; 27762306a36Sopenharmony_ci dev_dbg(dev, 27862306a36Sopenharmony_ci "Decreasing clock divider to %d for fan %d\n", 27962306a36Sopenharmony_ci FAN_DIV_FROM_REG(data->fan_status[nr]), 28062306a36Sopenharmony_ci nr + 1); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Write new fan min if it changed */ 28562306a36Sopenharmony_ci if (old_min != data->fan_min[nr]) { 28662306a36Sopenharmony_ci pc87360_write_value(data, LD_FAN, NO_BANK, 28762306a36Sopenharmony_ci PC87360_REG_FAN_MIN(nr), 28862306a36Sopenharmony_ci data->fan_min[nr]); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic struct pc87360_data *pc87360_update_device(struct device *dev) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 29562306a36Sopenharmony_ci u8 i; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci mutex_lock(&data->update_lock); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { 30062306a36Sopenharmony_ci dev_dbg(dev, "Data update\n"); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Fans */ 30362306a36Sopenharmony_ci for (i = 0; i < data->fannr; i++) { 30462306a36Sopenharmony_ci if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { 30562306a36Sopenharmony_ci data->fan_status[i] = 30662306a36Sopenharmony_ci pc87360_read_value(data, LD_FAN, 30762306a36Sopenharmony_ci NO_BANK, PC87360_REG_FAN_STATUS(i)); 30862306a36Sopenharmony_ci data->fan[i] = pc87360_read_value(data, LD_FAN, 30962306a36Sopenharmony_ci NO_BANK, PC87360_REG_FAN(i)); 31062306a36Sopenharmony_ci data->fan_min[i] = pc87360_read_value(data, 31162306a36Sopenharmony_ci LD_FAN, NO_BANK, 31262306a36Sopenharmony_ci PC87360_REG_FAN_MIN(i)); 31362306a36Sopenharmony_ci /* Change clock divider if needed */ 31462306a36Sopenharmony_ci pc87360_autodiv(dev, i); 31562306a36Sopenharmony_ci /* Clear bits and write new divider */ 31662306a36Sopenharmony_ci pc87360_write_value(data, LD_FAN, NO_BANK, 31762306a36Sopenharmony_ci PC87360_REG_FAN_STATUS(i), 31862306a36Sopenharmony_ci data->fan_status[i]); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci if (FAN_CONFIG_CONTROL(data->fan_conf, i)) 32162306a36Sopenharmony_ci data->pwm[i] = pc87360_read_value(data, LD_FAN, 32262306a36Sopenharmony_ci NO_BANK, PC87360_REG_PWM(i)); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Voltages */ 32662306a36Sopenharmony_ci for (i = 0; i < data->innr; i++) { 32762306a36Sopenharmony_ci data->in_status[i] = pc87360_read_value(data, LD_IN, i, 32862306a36Sopenharmony_ci PC87365_REG_IN_STATUS); 32962306a36Sopenharmony_ci /* Clear bits */ 33062306a36Sopenharmony_ci pc87360_write_value(data, LD_IN, i, 33162306a36Sopenharmony_ci PC87365_REG_IN_STATUS, 33262306a36Sopenharmony_ci data->in_status[i]); 33362306a36Sopenharmony_ci if ((data->in_status[i] & CHAN_READY) == CHAN_READY) { 33462306a36Sopenharmony_ci data->in[i] = pc87360_read_value(data, LD_IN, 33562306a36Sopenharmony_ci i, PC87365_REG_IN); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci if (data->in_status[i] & CHAN_ENA) { 33862306a36Sopenharmony_ci data->in_min[i] = pc87360_read_value(data, 33962306a36Sopenharmony_ci LD_IN, i, 34062306a36Sopenharmony_ci PC87365_REG_IN_MIN); 34162306a36Sopenharmony_ci data->in_max[i] = pc87360_read_value(data, 34262306a36Sopenharmony_ci LD_IN, i, 34362306a36Sopenharmony_ci PC87365_REG_IN_MAX); 34462306a36Sopenharmony_ci if (i >= 11) 34562306a36Sopenharmony_ci data->in_crit[i-11] = 34662306a36Sopenharmony_ci pc87360_read_value(data, LD_IN, 34762306a36Sopenharmony_ci i, PC87365_REG_TEMP_CRIT); 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci if (data->innr) { 35162306a36Sopenharmony_ci data->in_alarms = pc87360_read_value(data, LD_IN, 35262306a36Sopenharmony_ci NO_BANK, PC87365_REG_IN_ALARMS1) 35362306a36Sopenharmony_ci | ((pc87360_read_value(data, LD_IN, 35462306a36Sopenharmony_ci NO_BANK, PC87365_REG_IN_ALARMS2) 35562306a36Sopenharmony_ci & 0x07) << 8); 35662306a36Sopenharmony_ci data->vid = (data->vid_conf & 0xE0) ? 35762306a36Sopenharmony_ci pc87360_read_value(data, LD_IN, 35862306a36Sopenharmony_ci NO_BANK, PC87365_REG_VID) : 0x1F; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* Temperatures */ 36262306a36Sopenharmony_ci for (i = 0; i < data->tempnr; i++) { 36362306a36Sopenharmony_ci data->temp_status[i] = pc87360_read_value(data, 36462306a36Sopenharmony_ci LD_TEMP, i, 36562306a36Sopenharmony_ci PC87365_REG_TEMP_STATUS); 36662306a36Sopenharmony_ci /* Clear bits */ 36762306a36Sopenharmony_ci pc87360_write_value(data, LD_TEMP, i, 36862306a36Sopenharmony_ci PC87365_REG_TEMP_STATUS, 36962306a36Sopenharmony_ci data->temp_status[i]); 37062306a36Sopenharmony_ci if ((data->temp_status[i] & CHAN_READY) == CHAN_READY) { 37162306a36Sopenharmony_ci data->temp[i] = pc87360_read_value(data, 37262306a36Sopenharmony_ci LD_TEMP, i, 37362306a36Sopenharmony_ci PC87365_REG_TEMP); 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci if (data->temp_status[i] & CHAN_ENA) { 37662306a36Sopenharmony_ci data->temp_min[i] = pc87360_read_value(data, 37762306a36Sopenharmony_ci LD_TEMP, i, 37862306a36Sopenharmony_ci PC87365_REG_TEMP_MIN); 37962306a36Sopenharmony_ci data->temp_max[i] = pc87360_read_value(data, 38062306a36Sopenharmony_ci LD_TEMP, i, 38162306a36Sopenharmony_ci PC87365_REG_TEMP_MAX); 38262306a36Sopenharmony_ci data->temp_crit[i] = pc87360_read_value(data, 38362306a36Sopenharmony_ci LD_TEMP, i, 38462306a36Sopenharmony_ci PC87365_REG_TEMP_CRIT); 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci if (data->tempnr) { 38862306a36Sopenharmony_ci data->temp_alarms = pc87360_read_value(data, LD_TEMP, 38962306a36Sopenharmony_ci NO_BANK, PC87365_REG_TEMP_ALARMS) 39062306a36Sopenharmony_ci & 0x3F; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci data->last_updated = jiffies; 39462306a36Sopenharmony_ci data->valid = true; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return data; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic ssize_t in_input_show(struct device *dev, 40362306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 40662306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 40762306a36Sopenharmony_ci return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index], 40862306a36Sopenharmony_ci data->in_vref)); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic struct sensor_device_attribute in_input[] = { 41262306a36Sopenharmony_ci SENSOR_ATTR_RO(in0_input, in_input, 0), 41362306a36Sopenharmony_ci SENSOR_ATTR_RO(in1_input, in_input, 1), 41462306a36Sopenharmony_ci SENSOR_ATTR_RO(in2_input, in_input, 2), 41562306a36Sopenharmony_ci SENSOR_ATTR_RO(in3_input, in_input, 3), 41662306a36Sopenharmony_ci SENSOR_ATTR_RO(in4_input, in_input, 4), 41762306a36Sopenharmony_ci SENSOR_ATTR_RO(in5_input, in_input, 5), 41862306a36Sopenharmony_ci SENSOR_ATTR_RO(in6_input, in_input, 6), 41962306a36Sopenharmony_ci SENSOR_ATTR_RO(in7_input, in_input, 7), 42062306a36Sopenharmony_ci SENSOR_ATTR_RO(in8_input, in_input, 8), 42162306a36Sopenharmony_ci SENSOR_ATTR_RO(in9_input, in_input, 9), 42262306a36Sopenharmony_ci SENSOR_ATTR_RO(in10_input, in_input, 10), 42362306a36Sopenharmony_ci}; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic ssize_t in_status_show(struct device *dev, 42662306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 42962306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 43062306a36Sopenharmony_ci return sprintf(buf, "%u\n", data->in_status[attr->index]); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic struct sensor_device_attribute in_status[] = { 43462306a36Sopenharmony_ci SENSOR_ATTR_RO(in0_status, in_status, 0), 43562306a36Sopenharmony_ci SENSOR_ATTR_RO(in1_status, in_status, 1), 43662306a36Sopenharmony_ci SENSOR_ATTR_RO(in2_status, in_status, 2), 43762306a36Sopenharmony_ci SENSOR_ATTR_RO(in3_status, in_status, 3), 43862306a36Sopenharmony_ci SENSOR_ATTR_RO(in4_status, in_status, 4), 43962306a36Sopenharmony_ci SENSOR_ATTR_RO(in5_status, in_status, 5), 44062306a36Sopenharmony_ci SENSOR_ATTR_RO(in6_status, in_status, 6), 44162306a36Sopenharmony_ci SENSOR_ATTR_RO(in7_status, in_status, 7), 44262306a36Sopenharmony_ci SENSOR_ATTR_RO(in8_status, in_status, 8), 44362306a36Sopenharmony_ci SENSOR_ATTR_RO(in9_status, in_status, 9), 44462306a36Sopenharmony_ci SENSOR_ATTR_RO(in10_status, in_status, 10), 44562306a36Sopenharmony_ci}; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic ssize_t in_min_show(struct device *dev, 44862306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 45162306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 45262306a36Sopenharmony_ci return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], 45362306a36Sopenharmony_ci data->in_vref)); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic ssize_t in_min_store(struct device *dev, 45762306a36Sopenharmony_ci struct device_attribute *devattr, const char *buf, 45862306a36Sopenharmony_ci size_t count) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 46162306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 46262306a36Sopenharmony_ci long val; 46362306a36Sopenharmony_ci int err; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci err = kstrtol(buf, 10, &val); 46662306a36Sopenharmony_ci if (err) 46762306a36Sopenharmony_ci return err; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci mutex_lock(&data->update_lock); 47062306a36Sopenharmony_ci data->in_min[attr->index] = IN_TO_REG(val, data->in_vref); 47162306a36Sopenharmony_ci pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_IN_MIN, 47262306a36Sopenharmony_ci data->in_min[attr->index]); 47362306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 47462306a36Sopenharmony_ci return count; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic struct sensor_device_attribute in_min[] = { 47862306a36Sopenharmony_ci SENSOR_ATTR_RW(in0_min, in_min, 0), 47962306a36Sopenharmony_ci SENSOR_ATTR_RW(in1_min, in_min, 1), 48062306a36Sopenharmony_ci SENSOR_ATTR_RW(in2_min, in_min, 2), 48162306a36Sopenharmony_ci SENSOR_ATTR_RW(in3_min, in_min, 3), 48262306a36Sopenharmony_ci SENSOR_ATTR_RW(in4_min, in_min, 4), 48362306a36Sopenharmony_ci SENSOR_ATTR_RW(in5_min, in_min, 5), 48462306a36Sopenharmony_ci SENSOR_ATTR_RW(in6_min, in_min, 6), 48562306a36Sopenharmony_ci SENSOR_ATTR_RW(in7_min, in_min, 7), 48662306a36Sopenharmony_ci SENSOR_ATTR_RW(in8_min, in_min, 8), 48762306a36Sopenharmony_ci SENSOR_ATTR_RW(in9_min, in_min, 9), 48862306a36Sopenharmony_ci SENSOR_ATTR_RW(in10_min, in_min, 10), 48962306a36Sopenharmony_ci}; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic ssize_t in_max_show(struct device *dev, 49262306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 49562306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 49662306a36Sopenharmony_ci return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], 49762306a36Sopenharmony_ci data->in_vref)); 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic ssize_t in_max_store(struct device *dev, 50162306a36Sopenharmony_ci struct device_attribute *devattr, const char *buf, 50262306a36Sopenharmony_ci size_t count) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 50562306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 50662306a36Sopenharmony_ci long val; 50762306a36Sopenharmony_ci int err; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci err = kstrtol(buf, 10, &val); 51062306a36Sopenharmony_ci if (err) 51162306a36Sopenharmony_ci return err; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci mutex_lock(&data->update_lock); 51462306a36Sopenharmony_ci data->in_max[attr->index] = IN_TO_REG(val, 51562306a36Sopenharmony_ci data->in_vref); 51662306a36Sopenharmony_ci pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_IN_MAX, 51762306a36Sopenharmony_ci data->in_max[attr->index]); 51862306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 51962306a36Sopenharmony_ci return count; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic struct sensor_device_attribute in_max[] = { 52362306a36Sopenharmony_ci SENSOR_ATTR_RW(in0_max, in_max, 0), 52462306a36Sopenharmony_ci SENSOR_ATTR_RW(in1_max, in_max, 1), 52562306a36Sopenharmony_ci SENSOR_ATTR_RW(in2_max, in_max, 2), 52662306a36Sopenharmony_ci SENSOR_ATTR_RW(in3_max, in_max, 3), 52762306a36Sopenharmony_ci SENSOR_ATTR_RW(in4_max, in_max, 4), 52862306a36Sopenharmony_ci SENSOR_ATTR_RW(in5_max, in_max, 5), 52962306a36Sopenharmony_ci SENSOR_ATTR_RW(in6_max, in_max, 6), 53062306a36Sopenharmony_ci SENSOR_ATTR_RW(in7_max, in_max, 7), 53162306a36Sopenharmony_ci SENSOR_ATTR_RW(in8_max, in_max, 8), 53262306a36Sopenharmony_ci SENSOR_ATTR_RW(in9_max, in_max, 9), 53362306a36Sopenharmony_ci SENSOR_ATTR_RW(in10_max, in_max, 10), 53462306a36Sopenharmony_ci}; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci/* (temp & vin) channel status register alarm bits (pdf sec.11.5.12) */ 53762306a36Sopenharmony_ci#define CHAN_ALM_MIN 0x02 /* min limit crossed */ 53862306a36Sopenharmony_ci#define CHAN_ALM_MAX 0x04 /* max limit exceeded */ 53962306a36Sopenharmony_ci#define TEMP_ALM_CRIT 0x08 /* temp crit exceeded (temp only) */ 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci/* 54262306a36Sopenharmony_ci * show_in_min/max_alarm() reads data from the per-channel status 54362306a36Sopenharmony_ci * register (sec 11.5.12), not the vin event status registers (sec 54462306a36Sopenharmony_ci * 11.5.2) that (legacy) show_in_alarm() resds (via data->in_alarms) 54562306a36Sopenharmony_ci */ 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic ssize_t in_min_alarm_show(struct device *dev, 54862306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 55162306a36Sopenharmony_ci unsigned nr = to_sensor_dev_attr(devattr)->index; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN)); 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic struct sensor_device_attribute in_min_alarm[] = { 55762306a36Sopenharmony_ci SENSOR_ATTR_RO(in0_min_alarm, in_min_alarm, 0), 55862306a36Sopenharmony_ci SENSOR_ATTR_RO(in1_min_alarm, in_min_alarm, 1), 55962306a36Sopenharmony_ci SENSOR_ATTR_RO(in2_min_alarm, in_min_alarm, 2), 56062306a36Sopenharmony_ci SENSOR_ATTR_RO(in3_min_alarm, in_min_alarm, 3), 56162306a36Sopenharmony_ci SENSOR_ATTR_RO(in4_min_alarm, in_min_alarm, 4), 56262306a36Sopenharmony_ci SENSOR_ATTR_RO(in5_min_alarm, in_min_alarm, 5), 56362306a36Sopenharmony_ci SENSOR_ATTR_RO(in6_min_alarm, in_min_alarm, 6), 56462306a36Sopenharmony_ci SENSOR_ATTR_RO(in7_min_alarm, in_min_alarm, 7), 56562306a36Sopenharmony_ci SENSOR_ATTR_RO(in8_min_alarm, in_min_alarm, 8), 56662306a36Sopenharmony_ci SENSOR_ATTR_RO(in9_min_alarm, in_min_alarm, 9), 56762306a36Sopenharmony_ci SENSOR_ATTR_RO(in10_min_alarm, in_min_alarm, 10), 56862306a36Sopenharmony_ci}; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic ssize_t in_max_alarm_show(struct device *dev, 57162306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 57462306a36Sopenharmony_ci unsigned nr = to_sensor_dev_attr(devattr)->index; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic struct sensor_device_attribute in_max_alarm[] = { 58062306a36Sopenharmony_ci SENSOR_ATTR_RO(in0_max_alarm, in_max_alarm, 0), 58162306a36Sopenharmony_ci SENSOR_ATTR_RO(in1_max_alarm, in_max_alarm, 1), 58262306a36Sopenharmony_ci SENSOR_ATTR_RO(in2_max_alarm, in_max_alarm, 2), 58362306a36Sopenharmony_ci SENSOR_ATTR_RO(in3_max_alarm, in_max_alarm, 3), 58462306a36Sopenharmony_ci SENSOR_ATTR_RO(in4_max_alarm, in_max_alarm, 4), 58562306a36Sopenharmony_ci SENSOR_ATTR_RO(in5_max_alarm, in_max_alarm, 5), 58662306a36Sopenharmony_ci SENSOR_ATTR_RO(in6_max_alarm, in_max_alarm, 6), 58762306a36Sopenharmony_ci SENSOR_ATTR_RO(in7_max_alarm, in_max_alarm, 7), 58862306a36Sopenharmony_ci SENSOR_ATTR_RO(in8_max_alarm, in_max_alarm, 8), 58962306a36Sopenharmony_ci SENSOR_ATTR_RO(in9_max_alarm, in_max_alarm, 9), 59062306a36Sopenharmony_ci SENSOR_ATTR_RO(in10_max_alarm, in_max_alarm, 10), 59162306a36Sopenharmony_ci}; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci#define VIN_UNIT_ATTRS(X) \ 59462306a36Sopenharmony_ci &in_input[X].dev_attr.attr, \ 59562306a36Sopenharmony_ci &in_status[X].dev_attr.attr, \ 59662306a36Sopenharmony_ci &in_min[X].dev_attr.attr, \ 59762306a36Sopenharmony_ci &in_max[X].dev_attr.attr, \ 59862306a36Sopenharmony_ci &in_min_alarm[X].dev_attr.attr, \ 59962306a36Sopenharmony_ci &in_max_alarm[X].dev_attr.attr 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic ssize_t cpu0_vid_show(struct device *dev, 60262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 60562306a36Sopenharmony_ci return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm)); 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(cpu0_vid); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic ssize_t vrm_show(struct device *dev, struct device_attribute *attr, 61062306a36Sopenharmony_ci char *buf) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 61362306a36Sopenharmony_ci return sprintf(buf, "%u\n", data->vrm); 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic ssize_t vrm_store(struct device *dev, struct device_attribute *attr, 61762306a36Sopenharmony_ci const char *buf, size_t count) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 62062306a36Sopenharmony_ci unsigned long val; 62162306a36Sopenharmony_ci int err; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci err = kstrtoul(buf, 10, &val); 62462306a36Sopenharmony_ci if (err) 62562306a36Sopenharmony_ci return err; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (val > 255) 62862306a36Sopenharmony_ci return -EINVAL; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci data->vrm = val; 63162306a36Sopenharmony_ci return count; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(vrm); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic ssize_t alarms_in_show(struct device *dev, 63662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 63962306a36Sopenharmony_ci return sprintf(buf, "%u\n", data->in_alarms); 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(alarms_in); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic struct attribute *pc8736x_vin_attr_array[] = { 64462306a36Sopenharmony_ci VIN_UNIT_ATTRS(0), 64562306a36Sopenharmony_ci VIN_UNIT_ATTRS(1), 64662306a36Sopenharmony_ci VIN_UNIT_ATTRS(2), 64762306a36Sopenharmony_ci VIN_UNIT_ATTRS(3), 64862306a36Sopenharmony_ci VIN_UNIT_ATTRS(4), 64962306a36Sopenharmony_ci VIN_UNIT_ATTRS(5), 65062306a36Sopenharmony_ci VIN_UNIT_ATTRS(6), 65162306a36Sopenharmony_ci VIN_UNIT_ATTRS(7), 65262306a36Sopenharmony_ci VIN_UNIT_ATTRS(8), 65362306a36Sopenharmony_ci VIN_UNIT_ATTRS(9), 65462306a36Sopenharmony_ci VIN_UNIT_ATTRS(10), 65562306a36Sopenharmony_ci &dev_attr_cpu0_vid.attr, 65662306a36Sopenharmony_ci &dev_attr_vrm.attr, 65762306a36Sopenharmony_ci &dev_attr_alarms_in.attr, 65862306a36Sopenharmony_ci NULL 65962306a36Sopenharmony_ci}; 66062306a36Sopenharmony_cistatic const struct attribute_group pc8736x_vin_group = { 66162306a36Sopenharmony_ci .attrs = pc8736x_vin_attr_array, 66262306a36Sopenharmony_ci}; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic ssize_t therm_input_show(struct device *dev, 66562306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 66862306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 66962306a36Sopenharmony_ci return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index], 67062306a36Sopenharmony_ci data->in_vref)); 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci/* 67462306a36Sopenharmony_ci * the +11 term below reflects the fact that VLM units 11,12,13 are 67562306a36Sopenharmony_ci * used in the chip to measure voltage across the thermistors 67662306a36Sopenharmony_ci */ 67762306a36Sopenharmony_cistatic struct sensor_device_attribute therm_input[] = { 67862306a36Sopenharmony_ci SENSOR_ATTR_RO(temp4_input, therm_input, 0 + 11), 67962306a36Sopenharmony_ci SENSOR_ATTR_RO(temp5_input, therm_input, 1 + 11), 68062306a36Sopenharmony_ci SENSOR_ATTR_RO(temp6_input, therm_input, 2 + 11), 68162306a36Sopenharmony_ci}; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic ssize_t therm_status_show(struct device *dev, 68462306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 68762306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 68862306a36Sopenharmony_ci return sprintf(buf, "%u\n", data->in_status[attr->index]); 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic struct sensor_device_attribute therm_status[] = { 69262306a36Sopenharmony_ci SENSOR_ATTR_RO(temp4_status, therm_status, 0 + 11), 69362306a36Sopenharmony_ci SENSOR_ATTR_RO(temp5_status, therm_status, 1 + 11), 69462306a36Sopenharmony_ci SENSOR_ATTR_RO(temp6_status, therm_status, 2 + 11), 69562306a36Sopenharmony_ci}; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic ssize_t therm_min_show(struct device *dev, 69862306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 70162306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 70262306a36Sopenharmony_ci return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], 70362306a36Sopenharmony_ci data->in_vref)); 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic ssize_t therm_min_store(struct device *dev, 70762306a36Sopenharmony_ci struct device_attribute *devattr, 70862306a36Sopenharmony_ci const char *buf, size_t count) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 71162306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 71262306a36Sopenharmony_ci long val; 71362306a36Sopenharmony_ci int err; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci err = kstrtol(buf, 10, &val); 71662306a36Sopenharmony_ci if (err) 71762306a36Sopenharmony_ci return err; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci mutex_lock(&data->update_lock); 72062306a36Sopenharmony_ci data->in_min[attr->index] = IN_TO_REG(val, data->in_vref); 72162306a36Sopenharmony_ci pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_TEMP_MIN, 72262306a36Sopenharmony_ci data->in_min[attr->index]); 72362306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 72462306a36Sopenharmony_ci return count; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic struct sensor_device_attribute therm_min[] = { 72862306a36Sopenharmony_ci SENSOR_ATTR_RW(temp4_min, therm_min, 0 + 11), 72962306a36Sopenharmony_ci SENSOR_ATTR_RW(temp5_min, therm_min, 1 + 11), 73062306a36Sopenharmony_ci SENSOR_ATTR_RW(temp6_min, therm_min, 2 + 11), 73162306a36Sopenharmony_ci}; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic ssize_t therm_max_show(struct device *dev, 73462306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 73762306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 73862306a36Sopenharmony_ci return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], 73962306a36Sopenharmony_ci data->in_vref)); 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic ssize_t therm_max_store(struct device *dev, 74362306a36Sopenharmony_ci struct device_attribute *devattr, 74462306a36Sopenharmony_ci const char *buf, size_t count) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 74762306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 74862306a36Sopenharmony_ci long val; 74962306a36Sopenharmony_ci int err; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci err = kstrtol(buf, 10, &val); 75262306a36Sopenharmony_ci if (err) 75362306a36Sopenharmony_ci return err; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci mutex_lock(&data->update_lock); 75662306a36Sopenharmony_ci data->in_max[attr->index] = IN_TO_REG(val, data->in_vref); 75762306a36Sopenharmony_ci pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_TEMP_MAX, 75862306a36Sopenharmony_ci data->in_max[attr->index]); 75962306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 76062306a36Sopenharmony_ci return count; 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic struct sensor_device_attribute therm_max[] = { 76462306a36Sopenharmony_ci SENSOR_ATTR_RW(temp4_max, therm_max, 0 + 11), 76562306a36Sopenharmony_ci SENSOR_ATTR_RW(temp5_max, therm_max, 1 + 11), 76662306a36Sopenharmony_ci SENSOR_ATTR_RW(temp6_max, therm_max, 2 + 11), 76762306a36Sopenharmony_ci}; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic ssize_t therm_crit_show(struct device *dev, 77062306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 77362306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 77462306a36Sopenharmony_ci return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[attr->index-11], 77562306a36Sopenharmony_ci data->in_vref)); 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic ssize_t therm_crit_store(struct device *dev, 77962306a36Sopenharmony_ci struct device_attribute *devattr, 78062306a36Sopenharmony_ci const char *buf, size_t count) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 78362306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 78462306a36Sopenharmony_ci long val; 78562306a36Sopenharmony_ci int err; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci err = kstrtol(buf, 10, &val); 78862306a36Sopenharmony_ci if (err) 78962306a36Sopenharmony_ci return err; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci mutex_lock(&data->update_lock); 79262306a36Sopenharmony_ci data->in_crit[attr->index-11] = IN_TO_REG(val, data->in_vref); 79362306a36Sopenharmony_ci pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_TEMP_CRIT, 79462306a36Sopenharmony_ci data->in_crit[attr->index-11]); 79562306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 79662306a36Sopenharmony_ci return count; 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic struct sensor_device_attribute therm_crit[] = { 80062306a36Sopenharmony_ci SENSOR_ATTR_RW(temp4_crit, therm_crit, 0 + 11), 80162306a36Sopenharmony_ci SENSOR_ATTR_RW(temp5_crit, therm_crit, 1 + 11), 80262306a36Sopenharmony_ci SENSOR_ATTR_RW(temp6_crit, therm_crit, 2 + 11), 80362306a36Sopenharmony_ci}; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci/* 80662306a36Sopenharmony_ci * show_therm_min/max_alarm() reads data from the per-channel voltage 80762306a36Sopenharmony_ci * status register (sec 11.5.12) 80862306a36Sopenharmony_ci */ 80962306a36Sopenharmony_cistatic ssize_t therm_min_alarm_show(struct device *dev, 81062306a36Sopenharmony_ci struct device_attribute *devattr, 81162306a36Sopenharmony_ci char *buf) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 81462306a36Sopenharmony_ci unsigned nr = to_sensor_dev_attr(devattr)->index; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN)); 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic struct sensor_device_attribute therm_min_alarm[] = { 82062306a36Sopenharmony_ci SENSOR_ATTR_RO(temp4_min_alarm, therm_min_alarm, 0 + 11), 82162306a36Sopenharmony_ci SENSOR_ATTR_RO(temp5_min_alarm, therm_min_alarm, 1 + 11), 82262306a36Sopenharmony_ci SENSOR_ATTR_RO(temp6_min_alarm, therm_min_alarm, 2 + 11), 82362306a36Sopenharmony_ci}; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic ssize_t therm_max_alarm_show(struct device *dev, 82662306a36Sopenharmony_ci struct device_attribute *devattr, 82762306a36Sopenharmony_ci char *buf) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 83062306a36Sopenharmony_ci unsigned nr = to_sensor_dev_attr(devattr)->index; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic struct sensor_device_attribute therm_max_alarm[] = { 83662306a36Sopenharmony_ci SENSOR_ATTR_RO(temp4_max_alarm, therm_max_alarm, 0 + 11), 83762306a36Sopenharmony_ci SENSOR_ATTR_RO(temp5_max_alarm, therm_max_alarm, 1 + 11), 83862306a36Sopenharmony_ci SENSOR_ATTR_RO(temp6_max_alarm, therm_max_alarm, 2 + 11), 83962306a36Sopenharmony_ci}; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic ssize_t therm_crit_alarm_show(struct device *dev, 84262306a36Sopenharmony_ci struct device_attribute *devattr, 84362306a36Sopenharmony_ci char *buf) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 84662306a36Sopenharmony_ci unsigned nr = to_sensor_dev_attr(devattr)->index; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci return sprintf(buf, "%u\n", !!(data->in_status[nr] & TEMP_ALM_CRIT)); 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic struct sensor_device_attribute therm_crit_alarm[] = { 85262306a36Sopenharmony_ci SENSOR_ATTR_RO(temp4_crit_alarm, therm_crit_alarm, 0 + 11), 85362306a36Sopenharmony_ci SENSOR_ATTR_RO(temp5_crit_alarm, therm_crit_alarm, 1 + 11), 85462306a36Sopenharmony_ci SENSOR_ATTR_RO(temp6_crit_alarm, therm_crit_alarm, 2 + 11), 85562306a36Sopenharmony_ci}; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci#define THERM_UNIT_ATTRS(X) \ 85862306a36Sopenharmony_ci &therm_input[X].dev_attr.attr, \ 85962306a36Sopenharmony_ci &therm_status[X].dev_attr.attr, \ 86062306a36Sopenharmony_ci &therm_min[X].dev_attr.attr, \ 86162306a36Sopenharmony_ci &therm_max[X].dev_attr.attr, \ 86262306a36Sopenharmony_ci &therm_crit[X].dev_attr.attr, \ 86362306a36Sopenharmony_ci &therm_min_alarm[X].dev_attr.attr, \ 86462306a36Sopenharmony_ci &therm_max_alarm[X].dev_attr.attr, \ 86562306a36Sopenharmony_ci &therm_crit_alarm[X].dev_attr.attr 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic struct attribute *pc8736x_therm_attr_array[] = { 86862306a36Sopenharmony_ci THERM_UNIT_ATTRS(0), 86962306a36Sopenharmony_ci THERM_UNIT_ATTRS(1), 87062306a36Sopenharmony_ci THERM_UNIT_ATTRS(2), 87162306a36Sopenharmony_ci NULL 87262306a36Sopenharmony_ci}; 87362306a36Sopenharmony_cistatic const struct attribute_group pc8736x_therm_group = { 87462306a36Sopenharmony_ci .attrs = pc8736x_therm_attr_array, 87562306a36Sopenharmony_ci}; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_cistatic ssize_t temp_input_show(struct device *dev, 87862306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 88162306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 88262306a36Sopenharmony_ci return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic struct sensor_device_attribute temp_input[] = { 88662306a36Sopenharmony_ci SENSOR_ATTR_RO(temp1_input, temp_input, 0), 88762306a36Sopenharmony_ci SENSOR_ATTR_RO(temp2_input, temp_input, 1), 88862306a36Sopenharmony_ci SENSOR_ATTR_RO(temp3_input, temp_input, 2), 88962306a36Sopenharmony_ci}; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic ssize_t temp_status_show(struct device *dev, 89262306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 89562306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 89662306a36Sopenharmony_ci return sprintf(buf, "%d\n", data->temp_status[attr->index]); 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic struct sensor_device_attribute temp_status[] = { 90062306a36Sopenharmony_ci SENSOR_ATTR_RO(temp1_status, temp_status, 0), 90162306a36Sopenharmony_ci SENSOR_ATTR_RO(temp2_status, temp_status, 1), 90262306a36Sopenharmony_ci SENSOR_ATTR_RO(temp3_status, temp_status, 2), 90362306a36Sopenharmony_ci}; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic ssize_t temp_min_show(struct device *dev, 90662306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 90962306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 91062306a36Sopenharmony_ci return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[attr->index])); 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic ssize_t temp_min_store(struct device *dev, 91462306a36Sopenharmony_ci struct device_attribute *devattr, 91562306a36Sopenharmony_ci const char *buf, size_t count) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 91862306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 91962306a36Sopenharmony_ci long val; 92062306a36Sopenharmony_ci int err; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci err = kstrtol(buf, 10, &val); 92362306a36Sopenharmony_ci if (err) 92462306a36Sopenharmony_ci return err; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci mutex_lock(&data->update_lock); 92762306a36Sopenharmony_ci data->temp_min[attr->index] = TEMP_TO_REG(val); 92862306a36Sopenharmony_ci pc87360_write_value(data, LD_TEMP, attr->index, PC87365_REG_TEMP_MIN, 92962306a36Sopenharmony_ci data->temp_min[attr->index]); 93062306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 93162306a36Sopenharmony_ci return count; 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic struct sensor_device_attribute temp_min[] = { 93562306a36Sopenharmony_ci SENSOR_ATTR_RW(temp1_min, temp_min, 0), 93662306a36Sopenharmony_ci SENSOR_ATTR_RW(temp2_min, temp_min, 1), 93762306a36Sopenharmony_ci SENSOR_ATTR_RW(temp3_min, temp_min, 2), 93862306a36Sopenharmony_ci}; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic ssize_t temp_max_show(struct device *dev, 94162306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 94462306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 94562306a36Sopenharmony_ci return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[attr->index])); 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic ssize_t temp_max_store(struct device *dev, 94962306a36Sopenharmony_ci struct device_attribute *devattr, 95062306a36Sopenharmony_ci const char *buf, size_t count) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 95362306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 95462306a36Sopenharmony_ci long val; 95562306a36Sopenharmony_ci int err; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci err = kstrtol(buf, 10, &val); 95862306a36Sopenharmony_ci if (err) 95962306a36Sopenharmony_ci return err; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci mutex_lock(&data->update_lock); 96262306a36Sopenharmony_ci data->temp_max[attr->index] = TEMP_TO_REG(val); 96362306a36Sopenharmony_ci pc87360_write_value(data, LD_TEMP, attr->index, PC87365_REG_TEMP_MAX, 96462306a36Sopenharmony_ci data->temp_max[attr->index]); 96562306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 96662306a36Sopenharmony_ci return count; 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic struct sensor_device_attribute temp_max[] = { 97062306a36Sopenharmony_ci SENSOR_ATTR_RW(temp1_max, temp_max, 0), 97162306a36Sopenharmony_ci SENSOR_ATTR_RW(temp2_max, temp_max, 1), 97262306a36Sopenharmony_ci SENSOR_ATTR_RW(temp3_max, temp_max, 2), 97362306a36Sopenharmony_ci}; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic ssize_t temp_crit_show(struct device *dev, 97662306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 97762306a36Sopenharmony_ci{ 97862306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 97962306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 98062306a36Sopenharmony_ci return sprintf(buf, "%d\n", 98162306a36Sopenharmony_ci TEMP_FROM_REG(data->temp_crit[attr->index])); 98262306a36Sopenharmony_ci} 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic ssize_t temp_crit_store(struct device *dev, 98562306a36Sopenharmony_ci struct device_attribute *devattr, 98662306a36Sopenharmony_ci const char *buf, size_t count) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 98962306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 99062306a36Sopenharmony_ci long val; 99162306a36Sopenharmony_ci int err; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci err = kstrtol(buf, 10, &val); 99462306a36Sopenharmony_ci if (err) 99562306a36Sopenharmony_ci return err; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci mutex_lock(&data->update_lock); 99862306a36Sopenharmony_ci data->temp_crit[attr->index] = TEMP_TO_REG(val); 99962306a36Sopenharmony_ci pc87360_write_value(data, LD_TEMP, attr->index, PC87365_REG_TEMP_CRIT, 100062306a36Sopenharmony_ci data->temp_crit[attr->index]); 100162306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 100262306a36Sopenharmony_ci return count; 100362306a36Sopenharmony_ci} 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_cistatic struct sensor_device_attribute temp_crit[] = { 100662306a36Sopenharmony_ci SENSOR_ATTR_RW(temp1_crit, temp_crit, 0), 100762306a36Sopenharmony_ci SENSOR_ATTR_RW(temp2_crit, temp_crit, 1), 100862306a36Sopenharmony_ci SENSOR_ATTR_RW(temp3_crit, temp_crit, 2), 100962306a36Sopenharmony_ci}; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci/* 101262306a36Sopenharmony_ci * temp_min/max_alarm_show() reads data from the per-channel status 101362306a36Sopenharmony_ci * register (sec 12.3.7), not the temp event status registers (sec 101462306a36Sopenharmony_ci * 12.3.2) that show_temp_alarm() reads (via data->temp_alarms) 101562306a36Sopenharmony_ci */ 101662306a36Sopenharmony_cistatic ssize_t temp_min_alarm_show(struct device *dev, 101762306a36Sopenharmony_ci struct device_attribute *devattr, 101862306a36Sopenharmony_ci char *buf) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 102162306a36Sopenharmony_ci unsigned nr = to_sensor_dev_attr(devattr)->index; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MIN)); 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic struct sensor_device_attribute temp_min_alarm[] = { 102762306a36Sopenharmony_ci SENSOR_ATTR_RO(temp1_min_alarm, temp_min_alarm, 0), 102862306a36Sopenharmony_ci SENSOR_ATTR_RO(temp2_min_alarm, temp_min_alarm, 1), 102962306a36Sopenharmony_ci SENSOR_ATTR_RO(temp3_min_alarm, temp_min_alarm, 2), 103062306a36Sopenharmony_ci}; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_cistatic ssize_t temp_max_alarm_show(struct device *dev, 103362306a36Sopenharmony_ci struct device_attribute *devattr, 103462306a36Sopenharmony_ci char *buf) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 103762306a36Sopenharmony_ci unsigned nr = to_sensor_dev_attr(devattr)->index; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MAX)); 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_cistatic struct sensor_device_attribute temp_max_alarm[] = { 104362306a36Sopenharmony_ci SENSOR_ATTR_RO(temp1_max_alarm, temp_max_alarm, 0), 104462306a36Sopenharmony_ci SENSOR_ATTR_RO(temp2_max_alarm, temp_max_alarm, 1), 104562306a36Sopenharmony_ci SENSOR_ATTR_RO(temp3_max_alarm, temp_max_alarm, 2), 104662306a36Sopenharmony_ci}; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_cistatic ssize_t temp_crit_alarm_show(struct device *dev, 104962306a36Sopenharmony_ci struct device_attribute *devattr, 105062306a36Sopenharmony_ci char *buf) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 105362306a36Sopenharmony_ci unsigned nr = to_sensor_dev_attr(devattr)->index; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_ALM_CRIT)); 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_cistatic struct sensor_device_attribute temp_crit_alarm[] = { 105962306a36Sopenharmony_ci SENSOR_ATTR_RO(temp1_crit_alarm, temp_crit_alarm, 0), 106062306a36Sopenharmony_ci SENSOR_ATTR_RO(temp2_crit_alarm, temp_crit_alarm, 1), 106162306a36Sopenharmony_ci SENSOR_ATTR_RO(temp3_crit_alarm, temp_crit_alarm, 2), 106262306a36Sopenharmony_ci}; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci#define TEMP_FAULT 0x40 /* open diode */ 106562306a36Sopenharmony_cistatic ssize_t temp_fault_show(struct device *dev, 106662306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 106962306a36Sopenharmony_ci unsigned nr = to_sensor_dev_attr(devattr)->index; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_FAULT)); 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_cistatic struct sensor_device_attribute temp_fault[] = { 107562306a36Sopenharmony_ci SENSOR_ATTR_RO(temp1_fault, temp_fault, 0), 107662306a36Sopenharmony_ci SENSOR_ATTR_RO(temp2_fault, temp_fault, 1), 107762306a36Sopenharmony_ci SENSOR_ATTR_RO(temp3_fault, temp_fault, 2), 107862306a36Sopenharmony_ci}; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci#define TEMP_UNIT_ATTRS(X) \ 108162306a36Sopenharmony_ci{ &temp_input[X].dev_attr.attr, \ 108262306a36Sopenharmony_ci &temp_status[X].dev_attr.attr, \ 108362306a36Sopenharmony_ci &temp_min[X].dev_attr.attr, \ 108462306a36Sopenharmony_ci &temp_max[X].dev_attr.attr, \ 108562306a36Sopenharmony_ci &temp_crit[X].dev_attr.attr, \ 108662306a36Sopenharmony_ci &temp_min_alarm[X].dev_attr.attr, \ 108762306a36Sopenharmony_ci &temp_max_alarm[X].dev_attr.attr, \ 108862306a36Sopenharmony_ci &temp_crit_alarm[X].dev_attr.attr, \ 108962306a36Sopenharmony_ci &temp_fault[X].dev_attr.attr, \ 109062306a36Sopenharmony_ci NULL \ 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic struct attribute *pc8736x_temp_attr[][10] = { 109462306a36Sopenharmony_ci TEMP_UNIT_ATTRS(0), 109562306a36Sopenharmony_ci TEMP_UNIT_ATTRS(1), 109662306a36Sopenharmony_ci TEMP_UNIT_ATTRS(2) 109762306a36Sopenharmony_ci}; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_cistatic const struct attribute_group pc8736x_temp_attr_group[] = { 110062306a36Sopenharmony_ci { .attrs = pc8736x_temp_attr[0] }, 110162306a36Sopenharmony_ci { .attrs = pc8736x_temp_attr[1] }, 110262306a36Sopenharmony_ci { .attrs = pc8736x_temp_attr[2] } 110362306a36Sopenharmony_ci}; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_cistatic ssize_t alarms_temp_show(struct device *dev, 110662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 110962306a36Sopenharmony_ci return sprintf(buf, "%u\n", data->temp_alarms); 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(alarms_temp); 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_cistatic ssize_t fan_input_show(struct device *dev, 111562306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 111862306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 111962306a36Sopenharmony_ci return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[attr->index], 112062306a36Sopenharmony_ci FAN_DIV_FROM_REG(data->fan_status[attr->index]))); 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_cistatic struct sensor_device_attribute fan_input[] = { 112462306a36Sopenharmony_ci SENSOR_ATTR_RO(fan1_input, fan_input, 0), 112562306a36Sopenharmony_ci SENSOR_ATTR_RO(fan2_input, fan_input, 1), 112662306a36Sopenharmony_ci SENSOR_ATTR_RO(fan3_input, fan_input, 2), 112762306a36Sopenharmony_ci}; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic ssize_t fan_status_show(struct device *dev, 113062306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 113362306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 113462306a36Sopenharmony_ci return sprintf(buf, "%u\n", 113562306a36Sopenharmony_ci FAN_STATUS_FROM_REG(data->fan_status[attr->index])); 113662306a36Sopenharmony_ci} 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_cistatic struct sensor_device_attribute fan_status[] = { 113962306a36Sopenharmony_ci SENSOR_ATTR_RO(fan1_status, fan_status, 0), 114062306a36Sopenharmony_ci SENSOR_ATTR_RO(fan2_status, fan_status, 1), 114162306a36Sopenharmony_ci SENSOR_ATTR_RO(fan3_status, fan_status, 2), 114262306a36Sopenharmony_ci}; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_cistatic ssize_t fan_div_show(struct device *dev, 114562306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 114862306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 114962306a36Sopenharmony_ci return sprintf(buf, "%u\n", 115062306a36Sopenharmony_ci FAN_DIV_FROM_REG(data->fan_status[attr->index])); 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic struct sensor_device_attribute fan_div[] = { 115462306a36Sopenharmony_ci SENSOR_ATTR_RO(fan1_div, fan_div, 0), 115562306a36Sopenharmony_ci SENSOR_ATTR_RO(fan2_div, fan_div, 1), 115662306a36Sopenharmony_ci SENSOR_ATTR_RO(fan3_div, fan_div, 2), 115762306a36Sopenharmony_ci}; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic ssize_t fan_min_show(struct device *dev, 116062306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 116162306a36Sopenharmony_ci{ 116262306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 116362306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 116462306a36Sopenharmony_ci return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[attr->index], 116562306a36Sopenharmony_ci FAN_DIV_FROM_REG(data->fan_status[attr->index]))); 116662306a36Sopenharmony_ci} 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_cistatic ssize_t fan_min_store(struct device *dev, 116962306a36Sopenharmony_ci struct device_attribute *devattr, 117062306a36Sopenharmony_ci const char *buf, size_t count) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 117362306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 117462306a36Sopenharmony_ci long fan_min; 117562306a36Sopenharmony_ci int err; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci err = kstrtol(buf, 10, &fan_min); 117862306a36Sopenharmony_ci if (err) 117962306a36Sopenharmony_ci return err; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci mutex_lock(&data->update_lock); 118262306a36Sopenharmony_ci fan_min = FAN_TO_REG(fan_min, 118362306a36Sopenharmony_ci FAN_DIV_FROM_REG(data->fan_status[attr->index])); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci /* If it wouldn't fit, change clock divisor */ 118662306a36Sopenharmony_ci while (fan_min > 255 118762306a36Sopenharmony_ci && (data->fan_status[attr->index] & 0x60) != 0x60) { 118862306a36Sopenharmony_ci fan_min >>= 1; 118962306a36Sopenharmony_ci data->fan[attr->index] >>= 1; 119062306a36Sopenharmony_ci data->fan_status[attr->index] += 0x20; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci data->fan_min[attr->index] = fan_min > 255 ? 255 : fan_min; 119362306a36Sopenharmony_ci pc87360_write_value(data, LD_FAN, NO_BANK, 119462306a36Sopenharmony_ci PC87360_REG_FAN_MIN(attr->index), 119562306a36Sopenharmony_ci data->fan_min[attr->index]); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci /* Write new divider, preserve alarm bits */ 119862306a36Sopenharmony_ci pc87360_write_value(data, LD_FAN, NO_BANK, 119962306a36Sopenharmony_ci PC87360_REG_FAN_STATUS(attr->index), 120062306a36Sopenharmony_ci data->fan_status[attr->index] & 0xF9); 120162306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci return count; 120462306a36Sopenharmony_ci} 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_cistatic struct sensor_device_attribute fan_min[] = { 120762306a36Sopenharmony_ci SENSOR_ATTR_RW(fan1_min, fan_min, 0), 120862306a36Sopenharmony_ci SENSOR_ATTR_RW(fan2_min, fan_min, 1), 120962306a36Sopenharmony_ci SENSOR_ATTR_RW(fan3_min, fan_min, 2), 121062306a36Sopenharmony_ci}; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci#define FAN_UNIT_ATTRS(X) \ 121362306a36Sopenharmony_ci{ &fan_input[X].dev_attr.attr, \ 121462306a36Sopenharmony_ci &fan_status[X].dev_attr.attr, \ 121562306a36Sopenharmony_ci &fan_div[X].dev_attr.attr, \ 121662306a36Sopenharmony_ci &fan_min[X].dev_attr.attr, \ 121762306a36Sopenharmony_ci NULL \ 121862306a36Sopenharmony_ci} 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_cistatic struct attribute *pc8736x_fan_attr[][5] = { 122162306a36Sopenharmony_ci FAN_UNIT_ATTRS(0), 122262306a36Sopenharmony_ci FAN_UNIT_ATTRS(1), 122362306a36Sopenharmony_ci FAN_UNIT_ATTRS(2) 122462306a36Sopenharmony_ci}; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_cistatic const struct attribute_group pc8736x_fan_attr_group[] = { 122762306a36Sopenharmony_ci { .attrs = pc8736x_fan_attr[0], }, 122862306a36Sopenharmony_ci { .attrs = pc8736x_fan_attr[1], }, 122962306a36Sopenharmony_ci { .attrs = pc8736x_fan_attr[2], }, 123062306a36Sopenharmony_ci}; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_cistatic ssize_t pwm_show(struct device *dev, struct device_attribute *devattr, 123362306a36Sopenharmony_ci char *buf) 123462306a36Sopenharmony_ci{ 123562306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 123662306a36Sopenharmony_ci struct pc87360_data *data = pc87360_update_device(dev); 123762306a36Sopenharmony_ci return sprintf(buf, "%u\n", 123862306a36Sopenharmony_ci PWM_FROM_REG(data->pwm[attr->index], 123962306a36Sopenharmony_ci FAN_CONFIG_INVERT(data->fan_conf, 124062306a36Sopenharmony_ci attr->index))); 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cistatic ssize_t pwm_store(struct device *dev, struct device_attribute *devattr, 124462306a36Sopenharmony_ci const char *buf, size_t count) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); 124762306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 124862306a36Sopenharmony_ci long val; 124962306a36Sopenharmony_ci int err; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci err = kstrtol(buf, 10, &val); 125262306a36Sopenharmony_ci if (err) 125362306a36Sopenharmony_ci return err; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci mutex_lock(&data->update_lock); 125662306a36Sopenharmony_ci data->pwm[attr->index] = PWM_TO_REG(val, 125762306a36Sopenharmony_ci FAN_CONFIG_INVERT(data->fan_conf, attr->index)); 125862306a36Sopenharmony_ci pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(attr->index), 125962306a36Sopenharmony_ci data->pwm[attr->index]); 126062306a36Sopenharmony_ci mutex_unlock(&data->update_lock); 126162306a36Sopenharmony_ci return count; 126262306a36Sopenharmony_ci} 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_cistatic struct sensor_device_attribute pwm[] = { 126562306a36Sopenharmony_ci SENSOR_ATTR_RW(pwm1, pwm, 0), 126662306a36Sopenharmony_ci SENSOR_ATTR_RW(pwm2, pwm, 1), 126762306a36Sopenharmony_ci SENSOR_ATTR_RW(pwm3, pwm, 2), 126862306a36Sopenharmony_ci}; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_cistatic ssize_t name_show(struct device *dev, 127162306a36Sopenharmony_ci struct device_attribute *devattr, char *buf) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci struct pc87360_data *data = dev_get_drvdata(dev); 127462306a36Sopenharmony_ci return sprintf(buf, "%s\n", data->name); 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(name); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_cistatic void pc87360_remove_files(struct device *dev) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci int i; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci device_remove_file(dev, &dev_attr_name); 128462306a36Sopenharmony_ci device_remove_file(dev, &dev_attr_alarms_temp); 128562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pc8736x_temp_attr_group); i++) 128662306a36Sopenharmony_ci sysfs_remove_group(&dev->kobj, &pc8736x_temp_attr_group[i]); 128762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pc8736x_fan_attr_group); i++) { 128862306a36Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &pc8736x_fan_attr_group[i]); 128962306a36Sopenharmony_ci device_remove_file(dev, &pwm[i].dev_attr); 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci sysfs_remove_group(&dev->kobj, &pc8736x_therm_group); 129262306a36Sopenharmony_ci sysfs_remove_group(&dev->kobj, &pc8736x_vin_group); 129362306a36Sopenharmony_ci} 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_cistatic void pc87360_init_device(struct platform_device *pdev, 129662306a36Sopenharmony_ci int use_thermistors) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci struct pc87360_data *data = platform_get_drvdata(pdev); 129962306a36Sopenharmony_ci int i, nr; 130062306a36Sopenharmony_ci const u8 init_in[14] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, 2, 2 }; 130162306a36Sopenharmony_ci const u8 init_temp[3] = { 2, 2, 1 }; 130262306a36Sopenharmony_ci u8 reg; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci if (init >= 2 && data->innr) { 130562306a36Sopenharmony_ci reg = pc87360_read_value(data, LD_IN, NO_BANK, 130662306a36Sopenharmony_ci PC87365_REG_IN_CONVRATE); 130762306a36Sopenharmony_ci dev_info(&pdev->dev, 130862306a36Sopenharmony_ci "VLM conversion set to 1s period, 160us delay\n"); 130962306a36Sopenharmony_ci pc87360_write_value(data, LD_IN, NO_BANK, 131062306a36Sopenharmony_ci PC87365_REG_IN_CONVRATE, 131162306a36Sopenharmony_ci (reg & 0xC0) | 0x11); 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci nr = data->innr < 11 ? data->innr : 11; 131562306a36Sopenharmony_ci for (i = 0; i < nr; i++) { 131662306a36Sopenharmony_ci reg = pc87360_read_value(data, LD_IN, i, 131762306a36Sopenharmony_ci PC87365_REG_IN_STATUS); 131862306a36Sopenharmony_ci dev_dbg(&pdev->dev, "bios in%d status:0x%02x\n", i, reg); 131962306a36Sopenharmony_ci if (init >= init_in[i]) { 132062306a36Sopenharmony_ci /* Forcibly enable voltage channel */ 132162306a36Sopenharmony_ci if (!(reg & CHAN_ENA)) { 132262306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Forcibly enabling in%d\n", 132362306a36Sopenharmony_ci i); 132462306a36Sopenharmony_ci pc87360_write_value(data, LD_IN, i, 132562306a36Sopenharmony_ci PC87365_REG_IN_STATUS, 132662306a36Sopenharmony_ci (reg & 0x68) | 0x87); 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci } 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci /* 133262306a36Sopenharmony_ci * We can't blindly trust the Super-I/O space configuration bit, 133362306a36Sopenharmony_ci * most BIOS won't set it properly 133462306a36Sopenharmony_ci */ 133562306a36Sopenharmony_ci dev_dbg(&pdev->dev, "bios thermistors:%d\n", use_thermistors); 133662306a36Sopenharmony_ci for (i = 11; i < data->innr; i++) { 133762306a36Sopenharmony_ci reg = pc87360_read_value(data, LD_IN, i, 133862306a36Sopenharmony_ci PC87365_REG_TEMP_STATUS); 133962306a36Sopenharmony_ci use_thermistors = use_thermistors || (reg & CHAN_ENA); 134062306a36Sopenharmony_ci /* thermistors are temp[4-6], measured on vin[11-14] */ 134162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i-7, reg); 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci dev_dbg(&pdev->dev, "using thermistors:%d\n", use_thermistors); 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci i = use_thermistors ? 2 : 0; 134662306a36Sopenharmony_ci for (; i < data->tempnr; i++) { 134762306a36Sopenharmony_ci reg = pc87360_read_value(data, LD_TEMP, i, 134862306a36Sopenharmony_ci PC87365_REG_TEMP_STATUS); 134962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "bios temp%d_status:0x%02x\n", i + 1, reg); 135062306a36Sopenharmony_ci if (init >= init_temp[i]) { 135162306a36Sopenharmony_ci /* Forcibly enable temperature channel */ 135262306a36Sopenharmony_ci if (!(reg & CHAN_ENA)) { 135362306a36Sopenharmony_ci dev_dbg(&pdev->dev, 135462306a36Sopenharmony_ci "Forcibly enabling temp%d\n", i + 1); 135562306a36Sopenharmony_ci pc87360_write_value(data, LD_TEMP, i, 135662306a36Sopenharmony_ci PC87365_REG_TEMP_STATUS, 135762306a36Sopenharmony_ci 0xCF); 135862306a36Sopenharmony_ci } 135962306a36Sopenharmony_ci } 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci if (use_thermistors) { 136362306a36Sopenharmony_ci for (i = 11; i < data->innr; i++) { 136462306a36Sopenharmony_ci if (init >= init_in[i]) { 136562306a36Sopenharmony_ci /* 136662306a36Sopenharmony_ci * The pin may already be used by thermal 136762306a36Sopenharmony_ci * diodes 136862306a36Sopenharmony_ci */ 136962306a36Sopenharmony_ci reg = pc87360_read_value(data, LD_TEMP, 137062306a36Sopenharmony_ci (i - 11) / 2, PC87365_REG_TEMP_STATUS); 137162306a36Sopenharmony_ci if (reg & CHAN_ENA) { 137262306a36Sopenharmony_ci dev_dbg(&pdev->dev, 137362306a36Sopenharmony_ci "Skipping temp%d, pin already in use by temp%d\n", 137462306a36Sopenharmony_ci i - 7, (i - 11) / 2); 137562306a36Sopenharmony_ci continue; 137662306a36Sopenharmony_ci } 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci /* Forcibly enable thermistor channel */ 137962306a36Sopenharmony_ci reg = pc87360_read_value(data, LD_IN, i, 138062306a36Sopenharmony_ci PC87365_REG_IN_STATUS); 138162306a36Sopenharmony_ci if (!(reg & CHAN_ENA)) { 138262306a36Sopenharmony_ci dev_dbg(&pdev->dev, 138362306a36Sopenharmony_ci "Forcibly enabling temp%d\n", 138462306a36Sopenharmony_ci i - 7); 138562306a36Sopenharmony_ci pc87360_write_value(data, LD_IN, i, 138662306a36Sopenharmony_ci PC87365_REG_TEMP_STATUS, 138762306a36Sopenharmony_ci (reg & 0x60) | 0x8F); 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci } 139062306a36Sopenharmony_ci } 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci if (data->innr) { 139462306a36Sopenharmony_ci reg = pc87360_read_value(data, LD_IN, NO_BANK, 139562306a36Sopenharmony_ci PC87365_REG_IN_CONFIG); 139662306a36Sopenharmony_ci dev_dbg(&pdev->dev, "bios vin-cfg:0x%02x\n", reg); 139762306a36Sopenharmony_ci if (reg & CHAN_ENA) { 139862306a36Sopenharmony_ci dev_dbg(&pdev->dev, 139962306a36Sopenharmony_ci "Forcibly enabling monitoring (VLM)\n"); 140062306a36Sopenharmony_ci pc87360_write_value(data, LD_IN, NO_BANK, 140162306a36Sopenharmony_ci PC87365_REG_IN_CONFIG, 140262306a36Sopenharmony_ci reg & 0xFE); 140362306a36Sopenharmony_ci } 140462306a36Sopenharmony_ci } 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci if (data->tempnr) { 140762306a36Sopenharmony_ci reg = pc87360_read_value(data, LD_TEMP, NO_BANK, 140862306a36Sopenharmony_ci PC87365_REG_TEMP_CONFIG); 140962306a36Sopenharmony_ci dev_dbg(&pdev->dev, "bios temp-cfg:0x%02x\n", reg); 141062306a36Sopenharmony_ci if (reg & CHAN_ENA) { 141162306a36Sopenharmony_ci dev_dbg(&pdev->dev, 141262306a36Sopenharmony_ci "Forcibly enabling monitoring (TMS)\n"); 141362306a36Sopenharmony_ci pc87360_write_value(data, LD_TEMP, NO_BANK, 141462306a36Sopenharmony_ci PC87365_REG_TEMP_CONFIG, 141562306a36Sopenharmony_ci reg & 0xFE); 141662306a36Sopenharmony_ci } 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci if (init >= 2) { 141962306a36Sopenharmony_ci /* Chip config as documented by National Semi. */ 142062306a36Sopenharmony_ci pc87360_write_value(data, LD_TEMP, 0xF, 0xA, 0x08); 142162306a36Sopenharmony_ci /* 142262306a36Sopenharmony_ci * We voluntarily omit the bank here, in case the 142362306a36Sopenharmony_ci * sequence itself matters. It shouldn't be a problem, 142462306a36Sopenharmony_ci * since nobody else is supposed to access the 142562306a36Sopenharmony_ci * device at that point. 142662306a36Sopenharmony_ci */ 142762306a36Sopenharmony_ci pc87360_write_value(data, LD_TEMP, NO_BANK, 0xB, 0x04); 142862306a36Sopenharmony_ci pc87360_write_value(data, LD_TEMP, NO_BANK, 0xC, 0x35); 142962306a36Sopenharmony_ci pc87360_write_value(data, LD_TEMP, NO_BANK, 0xD, 0x05); 143062306a36Sopenharmony_ci pc87360_write_value(data, LD_TEMP, NO_BANK, 0xE, 0x05); 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ci } 143362306a36Sopenharmony_ci} 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_cistatic int pc87360_probe(struct platform_device *pdev) 143662306a36Sopenharmony_ci{ 143762306a36Sopenharmony_ci int i; 143862306a36Sopenharmony_ci struct pc87360_data *data; 143962306a36Sopenharmony_ci int err = 0; 144062306a36Sopenharmony_ci const char *name; 144162306a36Sopenharmony_ci int use_thermistors = 0; 144262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci data = devm_kzalloc(dev, sizeof(struct pc87360_data), GFP_KERNEL); 144562306a36Sopenharmony_ci if (!data) 144662306a36Sopenharmony_ci return -ENOMEM; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci switch (devid) { 144962306a36Sopenharmony_ci default: 145062306a36Sopenharmony_ci name = "pc87360"; 145162306a36Sopenharmony_ci data->fannr = 2; 145262306a36Sopenharmony_ci break; 145362306a36Sopenharmony_ci case 0xe8: 145462306a36Sopenharmony_ci name = "pc87363"; 145562306a36Sopenharmony_ci data->fannr = 2; 145662306a36Sopenharmony_ci break; 145762306a36Sopenharmony_ci case 0xe4: 145862306a36Sopenharmony_ci name = "pc87364"; 145962306a36Sopenharmony_ci data->fannr = 3; 146062306a36Sopenharmony_ci break; 146162306a36Sopenharmony_ci case 0xe5: 146262306a36Sopenharmony_ci name = "pc87365"; 146362306a36Sopenharmony_ci data->fannr = extra_isa[0] ? 3 : 0; 146462306a36Sopenharmony_ci data->innr = extra_isa[1] ? 11 : 0; 146562306a36Sopenharmony_ci data->tempnr = extra_isa[2] ? 2 : 0; 146662306a36Sopenharmony_ci break; 146762306a36Sopenharmony_ci case 0xe9: 146862306a36Sopenharmony_ci name = "pc87366"; 146962306a36Sopenharmony_ci data->fannr = extra_isa[0] ? 3 : 0; 147062306a36Sopenharmony_ci data->innr = extra_isa[1] ? 14 : 0; 147162306a36Sopenharmony_ci data->tempnr = extra_isa[2] ? 3 : 0; 147262306a36Sopenharmony_ci break; 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci data->name = name; 147662306a36Sopenharmony_ci mutex_init(&data->lock); 147762306a36Sopenharmony_ci mutex_init(&data->update_lock); 147862306a36Sopenharmony_ci platform_set_drvdata(pdev, data); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci for (i = 0; i < LDNI_MAX; i++) { 148162306a36Sopenharmony_ci data->address[i] = extra_isa[i]; 148262306a36Sopenharmony_ci if (data->address[i] 148362306a36Sopenharmony_ci && !devm_request_region(dev, extra_isa[i], PC87360_EXTENT, 148462306a36Sopenharmony_ci DRIVER_NAME)) { 148562306a36Sopenharmony_ci dev_err(dev, 148662306a36Sopenharmony_ci "Region 0x%x-0x%x already in use!\n", 148762306a36Sopenharmony_ci extra_isa[i], extra_isa[i]+PC87360_EXTENT-1); 148862306a36Sopenharmony_ci return -EBUSY; 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci /* Retrieve the fans configuration from Super-I/O space */ 149362306a36Sopenharmony_ci if (data->fannr) 149462306a36Sopenharmony_ci data->fan_conf = confreg[0] | (confreg[1] << 8); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci /* 149762306a36Sopenharmony_ci * Use the correct reference voltage 149862306a36Sopenharmony_ci * Unless both the VLM and the TMS logical devices agree to 149962306a36Sopenharmony_ci * use an external Vref, the internal one is used. 150062306a36Sopenharmony_ci */ 150162306a36Sopenharmony_ci if (data->innr) { 150262306a36Sopenharmony_ci i = pc87360_read_value(data, LD_IN, NO_BANK, 150362306a36Sopenharmony_ci PC87365_REG_IN_CONFIG); 150462306a36Sopenharmony_ci if (data->tempnr) { 150562306a36Sopenharmony_ci i &= pc87360_read_value(data, LD_TEMP, NO_BANK, 150662306a36Sopenharmony_ci PC87365_REG_TEMP_CONFIG); 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci data->in_vref = (i&0x02) ? 3025 : 2966; 150962306a36Sopenharmony_ci dev_dbg(dev, "Using %s reference voltage\n", 151062306a36Sopenharmony_ci (i&0x02) ? "external" : "internal"); 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci data->vid_conf = confreg[3]; 151362306a36Sopenharmony_ci data->vrm = vid_which_vrm(); 151462306a36Sopenharmony_ci } 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci /* Fan clock dividers may be needed before any data is read */ 151762306a36Sopenharmony_ci for (i = 0; i < data->fannr; i++) { 151862306a36Sopenharmony_ci if (FAN_CONFIG_MONITOR(data->fan_conf, i)) 151962306a36Sopenharmony_ci data->fan_status[i] = pc87360_read_value(data, 152062306a36Sopenharmony_ci LD_FAN, NO_BANK, 152162306a36Sopenharmony_ci PC87360_REG_FAN_STATUS(i)); 152262306a36Sopenharmony_ci } 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci if (init > 0) { 152562306a36Sopenharmony_ci if (devid == 0xe9 && data->address[1]) /* PC87366 */ 152662306a36Sopenharmony_ci use_thermistors = confreg[2] & 0x40; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci pc87360_init_device(pdev, use_thermistors); 152962306a36Sopenharmony_ci } 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci /* Register all-or-nothing sysfs groups */ 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci if (data->innr) { 153462306a36Sopenharmony_ci err = sysfs_create_group(&dev->kobj, &pc8736x_vin_group); 153562306a36Sopenharmony_ci if (err) 153662306a36Sopenharmony_ci goto error; 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci if (data->innr == 14) { 154062306a36Sopenharmony_ci err = sysfs_create_group(&dev->kobj, &pc8736x_therm_group); 154162306a36Sopenharmony_ci if (err) 154262306a36Sopenharmony_ci goto error; 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci /* create device attr-files for varying sysfs groups */ 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci if (data->tempnr) { 154862306a36Sopenharmony_ci for (i = 0; i < data->tempnr; i++) { 154962306a36Sopenharmony_ci err = sysfs_create_group(&dev->kobj, 155062306a36Sopenharmony_ci &pc8736x_temp_attr_group[i]); 155162306a36Sopenharmony_ci if (err) 155262306a36Sopenharmony_ci goto error; 155362306a36Sopenharmony_ci } 155462306a36Sopenharmony_ci err = device_create_file(dev, &dev_attr_alarms_temp); 155562306a36Sopenharmony_ci if (err) 155662306a36Sopenharmony_ci goto error; 155762306a36Sopenharmony_ci } 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci for (i = 0; i < data->fannr; i++) { 156062306a36Sopenharmony_ci if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { 156162306a36Sopenharmony_ci err = sysfs_create_group(&dev->kobj, 156262306a36Sopenharmony_ci &pc8736x_fan_attr_group[i]); 156362306a36Sopenharmony_ci if (err) 156462306a36Sopenharmony_ci goto error; 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci if (FAN_CONFIG_CONTROL(data->fan_conf, i)) { 156762306a36Sopenharmony_ci err = device_create_file(dev, &pwm[i].dev_attr); 156862306a36Sopenharmony_ci if (err) 156962306a36Sopenharmony_ci goto error; 157062306a36Sopenharmony_ci } 157162306a36Sopenharmony_ci } 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci err = device_create_file(dev, &dev_attr_name); 157462306a36Sopenharmony_ci if (err) 157562306a36Sopenharmony_ci goto error; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci data->hwmon_dev = hwmon_device_register(dev); 157862306a36Sopenharmony_ci if (IS_ERR(data->hwmon_dev)) { 157962306a36Sopenharmony_ci err = PTR_ERR(data->hwmon_dev); 158062306a36Sopenharmony_ci goto error; 158162306a36Sopenharmony_ci } 158262306a36Sopenharmony_ci return 0; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_cierror: 158562306a36Sopenharmony_ci pc87360_remove_files(dev); 158662306a36Sopenharmony_ci return err; 158762306a36Sopenharmony_ci} 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_cistatic int pc87360_remove(struct platform_device *pdev) 159062306a36Sopenharmony_ci{ 159162306a36Sopenharmony_ci struct pc87360_data *data = platform_get_drvdata(pdev); 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci hwmon_device_unregister(data->hwmon_dev); 159462306a36Sopenharmony_ci pc87360_remove_files(&pdev->dev); 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci return 0; 159762306a36Sopenharmony_ci} 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci/* 160062306a36Sopenharmony_ci * Driver data 160162306a36Sopenharmony_ci */ 160262306a36Sopenharmony_cistatic struct platform_driver pc87360_driver = { 160362306a36Sopenharmony_ci .driver = { 160462306a36Sopenharmony_ci .name = DRIVER_NAME, 160562306a36Sopenharmony_ci }, 160662306a36Sopenharmony_ci .probe = pc87360_probe, 160762306a36Sopenharmony_ci .remove = pc87360_remove, 160862306a36Sopenharmony_ci}; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci/* 161162306a36Sopenharmony_ci * Device detection, registration and update 161262306a36Sopenharmony_ci */ 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_cistatic int __init pc87360_find(int sioaddr, u8 *devid, 161562306a36Sopenharmony_ci unsigned short *addresses) 161662306a36Sopenharmony_ci{ 161762306a36Sopenharmony_ci u16 val; 161862306a36Sopenharmony_ci int i; 161962306a36Sopenharmony_ci int nrdev; /* logical device count */ 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci /* No superio_enter */ 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci /* Identify device */ 162462306a36Sopenharmony_ci val = force_id ? force_id : superio_inb(sioaddr, DEVID); 162562306a36Sopenharmony_ci switch (val) { 162662306a36Sopenharmony_ci case 0xE1: /* PC87360 */ 162762306a36Sopenharmony_ci case 0xE8: /* PC87363 */ 162862306a36Sopenharmony_ci case 0xE4: /* PC87364 */ 162962306a36Sopenharmony_ci nrdev = 1; 163062306a36Sopenharmony_ci break; 163162306a36Sopenharmony_ci case 0xE5: /* PC87365 */ 163262306a36Sopenharmony_ci case 0xE9: /* PC87366 */ 163362306a36Sopenharmony_ci nrdev = 3; 163462306a36Sopenharmony_ci break; 163562306a36Sopenharmony_ci default: 163662306a36Sopenharmony_ci superio_exit(sioaddr); 163762306a36Sopenharmony_ci return -ENODEV; 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci /* Remember the device id */ 164062306a36Sopenharmony_ci *devid = val; 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci for (i = 0; i < nrdev; i++) { 164362306a36Sopenharmony_ci /* select logical device */ 164462306a36Sopenharmony_ci superio_outb(sioaddr, DEV, logdev[i]); 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci val = superio_inb(sioaddr, ACT); 164762306a36Sopenharmony_ci if (!(val & 0x01)) { 164862306a36Sopenharmony_ci pr_info("Device 0x%02x not activated\n", logdev[i]); 164962306a36Sopenharmony_ci continue; 165062306a36Sopenharmony_ci } 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci val = (superio_inb(sioaddr, BASE) << 8) 165362306a36Sopenharmony_ci | superio_inb(sioaddr, BASE + 1); 165462306a36Sopenharmony_ci if (!val) { 165562306a36Sopenharmony_ci pr_info("Base address not set for device 0x%02x\n", 165662306a36Sopenharmony_ci logdev[i]); 165762306a36Sopenharmony_ci continue; 165862306a36Sopenharmony_ci } 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci addresses[i] = val; 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci if (i == 0) { /* Fans */ 166362306a36Sopenharmony_ci confreg[0] = superio_inb(sioaddr, 0xF0); 166462306a36Sopenharmony_ci confreg[1] = superio_inb(sioaddr, 0xF1); 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 1, 166762306a36Sopenharmony_ci (confreg[0] >> 2) & 1, (confreg[0] >> 3) & 1, 166862306a36Sopenharmony_ci (confreg[0] >> 4) & 1); 166962306a36Sopenharmony_ci pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 2, 167062306a36Sopenharmony_ci (confreg[0] >> 5) & 1, (confreg[0] >> 6) & 1, 167162306a36Sopenharmony_ci (confreg[0] >> 7) & 1); 167262306a36Sopenharmony_ci pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 3, 167362306a36Sopenharmony_ci confreg[1] & 1, (confreg[1] >> 1) & 1, 167462306a36Sopenharmony_ci (confreg[1] >> 2) & 1); 167562306a36Sopenharmony_ci } else if (i == 1) { /* Voltages */ 167662306a36Sopenharmony_ci /* Are we using thermistors? */ 167762306a36Sopenharmony_ci if (*devid == 0xE9) { /* PC87366 */ 167862306a36Sopenharmony_ci /* 167962306a36Sopenharmony_ci * These registers are not logical-device 168062306a36Sopenharmony_ci * specific, just that we won't need them if 168162306a36Sopenharmony_ci * we don't use the VLM device 168262306a36Sopenharmony_ci */ 168362306a36Sopenharmony_ci confreg[2] = superio_inb(sioaddr, 0x2B); 168462306a36Sopenharmony_ci confreg[3] = superio_inb(sioaddr, 0x25); 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci if (confreg[2] & 0x40) { 168762306a36Sopenharmony_ci pr_info("Using thermistors for temperature monitoring\n"); 168862306a36Sopenharmony_ci } 168962306a36Sopenharmony_ci if (confreg[3] & 0xE0) { 169062306a36Sopenharmony_ci pr_info("VID inputs routed (mode %u)\n", 169162306a36Sopenharmony_ci confreg[3] >> 5); 169262306a36Sopenharmony_ci } 169362306a36Sopenharmony_ci } 169462306a36Sopenharmony_ci } 169562306a36Sopenharmony_ci } 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci superio_exit(sioaddr); 169862306a36Sopenharmony_ci return 0; 169962306a36Sopenharmony_ci} 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_cistatic int __init pc87360_device_add(unsigned short address) 170262306a36Sopenharmony_ci{ 170362306a36Sopenharmony_ci struct resource res[3]; 170462306a36Sopenharmony_ci int err, i, res_count; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci pdev = platform_device_alloc("pc87360", address); 170762306a36Sopenharmony_ci if (!pdev) { 170862306a36Sopenharmony_ci err = -ENOMEM; 170962306a36Sopenharmony_ci pr_err("Device allocation failed\n"); 171062306a36Sopenharmony_ci goto exit; 171162306a36Sopenharmony_ci } 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci memset(res, 0, 3 * sizeof(struct resource)); 171462306a36Sopenharmony_ci res_count = 0; 171562306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 171662306a36Sopenharmony_ci if (!extra_isa[i]) 171762306a36Sopenharmony_ci continue; 171862306a36Sopenharmony_ci res[res_count].start = extra_isa[i]; 171962306a36Sopenharmony_ci res[res_count].end = extra_isa[i] + PC87360_EXTENT - 1; 172062306a36Sopenharmony_ci res[res_count].name = "pc87360"; 172162306a36Sopenharmony_ci res[res_count].flags = IORESOURCE_IO; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci err = acpi_check_resource_conflict(&res[res_count]); 172462306a36Sopenharmony_ci if (err) 172562306a36Sopenharmony_ci goto exit_device_put; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci res_count++; 172862306a36Sopenharmony_ci } 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci err = platform_device_add_resources(pdev, res, res_count); 173162306a36Sopenharmony_ci if (err) { 173262306a36Sopenharmony_ci pr_err("Device resources addition failed (%d)\n", err); 173362306a36Sopenharmony_ci goto exit_device_put; 173462306a36Sopenharmony_ci } 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci err = platform_device_add(pdev); 173762306a36Sopenharmony_ci if (err) { 173862306a36Sopenharmony_ci pr_err("Device addition failed (%d)\n", err); 173962306a36Sopenharmony_ci goto exit_device_put; 174062306a36Sopenharmony_ci } 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci return 0; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ciexit_device_put: 174562306a36Sopenharmony_ci platform_device_put(pdev); 174662306a36Sopenharmony_ciexit: 174762306a36Sopenharmony_ci return err; 174862306a36Sopenharmony_ci} 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_cistatic int __init pc87360_init(void) 175162306a36Sopenharmony_ci{ 175262306a36Sopenharmony_ci int err, i; 175362306a36Sopenharmony_ci unsigned short address = 0; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci if (pc87360_find(0x2e, &devid, extra_isa) 175662306a36Sopenharmony_ci && pc87360_find(0x4e, &devid, extra_isa)) { 175762306a36Sopenharmony_ci pr_warn("PC8736x not detected, module not inserted\n"); 175862306a36Sopenharmony_ci return -ENODEV; 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci /* Arbitrarily pick one of the addresses */ 176262306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 176362306a36Sopenharmony_ci if (extra_isa[i] != 0x0000) { 176462306a36Sopenharmony_ci address = extra_isa[i]; 176562306a36Sopenharmony_ci break; 176662306a36Sopenharmony_ci } 176762306a36Sopenharmony_ci } 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci if (address == 0x0000) { 177062306a36Sopenharmony_ci pr_warn("No active logical device, module not inserted\n"); 177162306a36Sopenharmony_ci return -ENODEV; 177262306a36Sopenharmony_ci } 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci err = platform_driver_register(&pc87360_driver); 177562306a36Sopenharmony_ci if (err) 177662306a36Sopenharmony_ci goto exit; 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci /* Sets global pdev as a side effect */ 177962306a36Sopenharmony_ci err = pc87360_device_add(address); 178062306a36Sopenharmony_ci if (err) 178162306a36Sopenharmony_ci goto exit_driver; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci return 0; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci exit_driver: 178662306a36Sopenharmony_ci platform_driver_unregister(&pc87360_driver); 178762306a36Sopenharmony_ci exit: 178862306a36Sopenharmony_ci return err; 178962306a36Sopenharmony_ci} 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_cistatic void __exit pc87360_exit(void) 179262306a36Sopenharmony_ci{ 179362306a36Sopenharmony_ci platform_device_unregister(pdev); 179462306a36Sopenharmony_ci platform_driver_unregister(&pc87360_driver); 179562306a36Sopenharmony_ci} 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ciMODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); 179862306a36Sopenharmony_ciMODULE_DESCRIPTION("PC8736x hardware monitor"); 179962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 180062306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRIVER_NAME); 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_cimodule_init(pc87360_init); 180362306a36Sopenharmony_cimodule_exit(pc87360_exit); 1804