/* * pc87360.c - Part of lm_sensors, Linux kernel modules * for hardware monitoring * Copyright (C) 2004, 2007 Jean Delvare * * Copied from smsc47m1.c: * Copyright (C) 2002 Mark D. Studebaker * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Supports the following chips: * * Chip #vin #fan #pwm #temp devid * PC87360 - 2 2 - 0xE1 * PC87363 - 2 2 - 0xE8 * PC87364 - 3 3 - 0xE4 * PC87365 11 3 3 2 0xE5 * PC87366 11 3 3 3-4 0xE9 * * This driver assumes that no more than one chip is present, and one of * the standard Super-I/O addresses is used (0x2E/0x2F or 0x4E/0x4F). */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include static u8 devid; static struct platform_device *pdev; static unsigned short extra_isa[3]; static u8 confreg[4]; static int init = 1; module_param(init, int, 0); MODULE_PARM_DESC(init, "Chip initialization level:\n" " 0: None\n" "*1: Forcibly enable internal voltage and temperature channels, except in9\n" " 2: Forcibly enable all voltage and temperature channels, except in9\n" " 3: Forcibly enable all voltage and temperature channels, including in9"); static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); /* * Super-I/O registers and operations */ #define DEV 0x07 /* Register: Logical device select */ #define DEVID 0x20 /* Register: Device ID */ #define ACT 0x30 /* Register: Device activation */ #define BASE 0x60 /* Register: Base address */ #define FSCM 0x09 /* Logical device: fans */ #define VLM 0x0d /* Logical device: voltages */ #define TMS 0x0e /* Logical device: temperatures */ #define LDNI_MAX 3 static const u8 logdev[LDNI_MAX] = { FSCM, VLM, TMS }; #define LD_FAN 0 #define LD_IN 1 #define LD_TEMP 2 static inline void superio_outb(int sioaddr, int reg, int val) { outb(reg, sioaddr); outb(val, sioaddr + 1); } static inline int superio_inb(int sioaddr, int reg) { outb(reg, sioaddr); return inb(sioaddr + 1); } static inline void superio_exit(int sioaddr) { outb(0x02, sioaddr); outb(0x02, sioaddr + 1); } /* * Logical devices */ #define PC87360_EXTENT 0x10 #define PC87365_REG_BANK 0x09 #define NO_BANK 0xff /* * Fan registers and conversions */ /* nr has to be 0 or 1 (PC87360/87363) or 2 (PC87364/87365/87366) */ #define PC87360_REG_PRESCALE(nr) (0x00 + 2 * (nr)) #define PC87360_REG_PWM(nr) (0x01 + 2 * (nr)) #define PC87360_REG_FAN_MIN(nr) (0x06 + 3 * (nr)) #define PC87360_REG_FAN(nr) (0x07 + 3 * (nr)) #define PC87360_REG_FAN_STATUS(nr) (0x08 + 3 * (nr)) #define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : \ 480000 / ((val) * (div))) #define FAN_TO_REG(val, div) ((val) <= 100 ? 0 : \ 480000 / ((val) * (div))) #define FAN_DIV_FROM_REG(val) (1 << (((val) >> 5) & 0x03)) #define FAN_STATUS_FROM_REG(val) ((val) & 0x07) #define FAN_CONFIG_MONITOR(val, nr) (((val) >> (2 + (nr) * 3)) & 1) #define FAN_CONFIG_CONTROL(val, nr) (((val) >> (3 + (nr) * 3)) & 1) #define FAN_CONFIG_INVERT(val, nr) (((val) >> (4 + (nr) * 3)) & 1) #define PWM_FROM_REG(val, inv) ((inv) ? 255 - (val) : (val)) static inline u8 PWM_TO_REG(int val, int inv) { if (inv) val = 255 - val; if (val < 0) return 0; if (val > 255) return 255; return val; } /* * Voltage registers and conversions */ #define PC87365_REG_IN_CONVRATE 0x07 #define PC87365_REG_IN_CONFIG 0x08 #define PC87365_REG_IN 0x0B #define PC87365_REG_IN_MIN 0x0D #define PC87365_REG_IN_MAX 0x0C #define PC87365_REG_IN_STATUS 0x0A #define PC87365_REG_IN_ALARMS1 0x00 #define PC87365_REG_IN_ALARMS2 0x01 #define PC87365_REG_VID 0x06 #define IN_FROM_REG(val, ref) (((val) * (ref) + 128) / 256) #define IN_TO_REG(val, ref) ((val) < 0 ? 0 : \ (val) * 256 >= (ref) * 255 ? 255 : \ ((val) * 256 + (ref) / 2) / (ref)) /* * Temperature registers and conversions */ #define PC87365_REG_TEMP_CONFIG 0x08 #define PC87365_REG_TEMP 0x0B #define PC87365_REG_TEMP_MIN 0x0D #define PC87365_REG_TEMP_MAX 0x0C #define PC87365_REG_TEMP_CRIT 0x0E #define PC87365_REG_TEMP_STATUS 0x0A #define PC87365_REG_TEMP_ALARMS 0x00 #define TEMP_FROM_REG(val) ((val) * 1000) #define TEMP_TO_REG(val) ((val) < -55000 ? -55 : \ (val) > 127000 ? 127 : \ (val) < 0 ? ((val) - 500) / 1000 : \ ((val) + 500) / 1000) /* * Device data */ struct pc87360_data { const char *name; struct device *hwmon_dev; struct mutex lock; struct mutex update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ int address[3]; u8 fannr, innr, tempnr; u8 fan[3]; /* Register value */ u8 fan_min[3]; /* Register value */ u8 fan_status[3]; /* Register value */ u8 pwm[3]; /* Register value */ u16 fan_conf; /* Configuration register values, combined */ u16 in_vref; /* 1 mV/bit */ u8 in[14]; /* Register value */ u8 in_min[14]; /* Register value */ u8 in_max[14]; /* Register value */ u8 in_crit[3]; /* Register value */ u8 in_status[14]; /* Register value */ u16 in_alarms; /* Register values, combined, masked */ u8 vid_conf; /* Configuration register value */ u8 vrm; u8 vid; /* Register value */ s8 temp[3]; /* Register value */ s8 temp_min[3]; /* Register value */ s8 temp_max[3]; /* Register value */ s8 temp_crit[3]; /* Register value */ u8 temp_status[3]; /* Register value */ u8 temp_alarms; /* Register value, masked */ }; /* * Functions declaration */ static int pc87360_probe(struct platform_device *pdev); static int pc87360_remove(struct platform_device *pdev); static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, u8 reg); static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, u8 reg, u8 value); static void pc87360_init_device(struct platform_device *pdev, int use_thermistors); static struct pc87360_data *pc87360_update_device(struct device *dev); /* * Driver data */ static struct platform_driver pc87360_driver = { .driver = { .name = "pc87360", }, .probe = pc87360_probe, .remove = pc87360_remove, }; /* * Sysfs stuff */ static ssize_t show_fan_input(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[attr->index], FAN_DIV_FROM_REG(data->fan_status[attr->index]))); } static ssize_t show_fan_min(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[attr->index], FAN_DIV_FROM_REG(data->fan_status[attr->index]))); } static ssize_t show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", FAN_DIV_FROM_REG(data->fan_status[attr->index])); } static ssize_t show_fan_status(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", FAN_STATUS_FROM_REG(data->fan_status[attr->index])); } static ssize_t set_fan_min(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); long fan_min; int err; err = kstrtol(buf, 10, &fan_min); if (err) return err; mutex_lock(&data->update_lock); fan_min = FAN_TO_REG(fan_min, FAN_DIV_FROM_REG(data->fan_status[attr->index])); /* If it wouldn't fit, change clock divisor */ while (fan_min > 255 && (data->fan_status[attr->index] & 0x60) != 0x60) { fan_min >>= 1; data->fan[attr->index] >>= 1; data->fan_status[attr->index] += 0x20; } data->fan_min[attr->index] = fan_min > 255 ? 255 : fan_min; pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_MIN(attr->index), data->fan_min[attr->index]); /* Write new divider, preserve alarm bits */ pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_STATUS(attr->index), data->fan_status[attr->index] & 0xF9); mutex_unlock(&data->update_lock); return count; } static struct sensor_device_attribute fan_input[] = { SENSOR_ATTR(fan1_input, S_IRUGO, show_fan_input, NULL, 0), SENSOR_ATTR(fan2_input, S_IRUGO, show_fan_input, NULL, 1), SENSOR_ATTR(fan3_input, S_IRUGO, show_fan_input, NULL, 2), }; static struct sensor_device_attribute fan_status[] = { SENSOR_ATTR(fan1_status, S_IRUGO, show_fan_status, NULL, 0), SENSOR_ATTR(fan2_status, S_IRUGO, show_fan_status, NULL, 1), SENSOR_ATTR(fan3_status, S_IRUGO, show_fan_status, NULL, 2), }; static struct sensor_device_attribute fan_div[] = { SENSOR_ATTR(fan1_div, S_IRUGO, show_fan_div, NULL, 0), SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1), SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2), }; static struct sensor_device_attribute fan_min[] = { SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 0), SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 1), SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 2), }; #define FAN_UNIT_ATTRS(X) \ { &fan_input[X].dev_attr.attr, \ &fan_status[X].dev_attr.attr, \ &fan_div[X].dev_attr.attr, \ &fan_min[X].dev_attr.attr, \ NULL \ } static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", PWM_FROM_REG(data->pwm[attr->index], FAN_CONFIG_INVERT(data->fan_conf, attr->index))); } static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); long val; int err; err = kstrtol(buf, 10, &val); if (err) return err; mutex_lock(&data->update_lock); data->pwm[attr->index] = PWM_TO_REG(val, FAN_CONFIG_INVERT(data->fan_conf, attr->index)); pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(attr->index), data->pwm[attr->index]); mutex_unlock(&data->update_lock); return count; } static struct sensor_device_attribute pwm[] = { SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0), SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1), SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2), }; static struct attribute *pc8736x_fan_attr[][5] = { FAN_UNIT_ATTRS(0), FAN_UNIT_ATTRS(1), FAN_UNIT_ATTRS(2) }; static const struct attribute_group pc8736x_fan_attr_group[] = { { .attrs = pc8736x_fan_attr[0], }, { .attrs = pc8736x_fan_attr[1], }, { .attrs = pc8736x_fan_attr[2], }, }; static ssize_t show_in_input(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index], data->in_vref)); } static ssize_t show_in_min(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], data->in_vref)); } static ssize_t show_in_max(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], data->in_vref)); } static ssize_t show_in_status(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", data->in_status[attr->index]); } static ssize_t set_in_min(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); long val; int err; err = kstrtol(buf, 10, &val); if (err) return err; mutex_lock(&data->update_lock); data->in_min[attr->index] = IN_TO_REG(val, data->in_vref); pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_IN_MIN, data->in_min[attr->index]); mutex_unlock(&data->update_lock); return count; } static ssize_t set_in_max(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); long val; int err; err = kstrtol(buf, 10, &val); if (err) return err; mutex_lock(&data->update_lock); data->in_max[attr->index] = IN_TO_REG(val, data->in_vref); pc87360_write_value(data, LD_IN, attr->index, PC87365_REG_IN_MAX, data->in_max[attr->index]); mutex_unlock(&data->update_lock); return count; } static struct sensor_device_attribute in_input[] = { SENSOR_ATTR(in0_input, S_IRUGO, show_in_input, NULL, 0), SENSOR_ATTR(in1_input, S_IRUGO, show_in_input, NULL, 1), SENSOR_ATTR(in2_input, S_IRUGO, show_in_input, NULL, 2), SENSOR_ATTR(in3_input, S_IRUGO, show_in_input, NULL, 3), SENSOR_ATTR(in4_input, S_IRUGO, show_in_input, NULL, 4), SENSOR_ATTR(in5_input, S_IRUGO, show_in_input, NULL, 5), SENSOR_ATTR(in6_input, S_IRUGO, show_in_input, NULL, 6), SENSOR_ATTR(in7_input, S_IRUGO, show_in_input, NULL, 7), SENSOR_ATTR(in8_input, S_IRUGO, show_in_input, NULL, 8), SENSOR_ATTR(in9_input, S_IRUGO, show_in_input, NULL, 9), SENSOR_ATTR(in10_input, S_IRUGO, show_in_input, NULL, 10), }; static struct sensor_device_attribute in_status[] = { SENSOR_ATTR(in0_status, S_IRUGO, show_in_status, NULL, 0), SENSOR_ATTR(in1_status, S_IRUGO, show_in_status, NULL, 1), SENSOR_ATTR(in2_status, S_IRUGO, show_in_status, NULL, 2), SENSOR_ATTR(in3_status, S_IRUGO, show_in_status, NULL, 3), SENSOR_ATTR(in4_status, S_IRUGO, show_in_status, NULL, 4), SENSOR_ATTR(in5_status, S_IRUGO, show_in_status, NULL, 5), SENSOR_ATTR(in6_status, S_IRUGO, show_in_status, NULL, 6), SENSOR_ATTR(in7_status, S_IRUGO, show_in_status, NULL, 7), SENSOR_ATTR(in8_status, S_IRUGO, show_in_status, NULL, 8), SENSOR_ATTR(in9_status, S_IRUGO, show_in_status, NULL, 9), SENSOR_ATTR(in10_status, S_IRUGO, show_in_status, NULL, 10), }; static struct sensor_device_attribute in_min[] = { SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, set_in_min, 0), SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, set_in_min, 1), SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, set_in_min, 2), SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, set_in_min, 3), SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, set_in_min, 4), SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, set_in_min, 5), SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, set_in_min, 6), SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, set_in_min, 7), SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, set_in_min, 8), SENSOR_ATTR(in9_min, S_IWUSR | S_IRUGO, show_in_min, set_in_min, 9), SENSOR_ATTR(in10_min, S_IWUSR | S_IRUGO, show_in_min, set_in_min, 10), }; static struct sensor_device_attribute in_max[] = { SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 0), SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 1), SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 2), SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 3), SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 4), SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 5), SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 6), SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 7), SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 8), SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 9), SENSOR_ATTR(in10_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 10), }; /* (temp & vin) channel status register alarm bits (pdf sec.11.5.12) */ #define CHAN_ALM_MIN 0x02 /* min limit crossed */ #define CHAN_ALM_MAX 0x04 /* max limit exceeded */ #define TEMP_ALM_CRIT 0x08 /* temp crit exceeded (temp only) */ /* * show_in_min/max_alarm() reads data from the per-channel status * register (sec 11.5.12), not the vin event status registers (sec * 11.5.2) that (legacy) show_in_alarm() resds (via data->in_alarms) */ static ssize_t show_in_min_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { struct pc87360_data *data = pc87360_update_device(dev); unsigned nr = to_sensor_dev_attr(devattr)->index; return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN)); } static ssize_t show_in_max_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { struct pc87360_data *data = pc87360_update_device(dev); unsigned nr = to_sensor_dev_attr(devattr)->index; return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); } static struct sensor_device_attribute in_min_alarm[] = { SENSOR_ATTR(in0_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 0), SENSOR_ATTR(in1_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 1), SENSOR_ATTR(in2_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 2), SENSOR_ATTR(in3_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 3), SENSOR_ATTR(in4_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 4), SENSOR_ATTR(in5_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 5), SENSOR_ATTR(in6_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 6), SENSOR_ATTR(in7_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 7), SENSOR_ATTR(in8_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 8), SENSOR_ATTR(in9_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 9), SENSOR_ATTR(in10_min_alarm, S_IRUGO, show_in_min_alarm, NULL, 10), }; static struct sensor_device_attribute in_max_alarm[] = { SENSOR_ATTR(in0_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 0), SENSOR_ATTR(in1_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 1), SENSOR_ATTR(in2_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 2), SENSOR_ATTR(in3_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 3), SENSOR_ATTR(in4_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 4), SENSOR_ATTR(in5_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 5), SENSOR_ATTR(in6_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 6), SENSOR_ATTR(in7_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 7), SENSOR_ATTR(in8_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 8), SENSOR_ATTR(in9_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 9), SENSOR_ATTR(in10_max_alarm, S_IRUGO, show_in_max_alarm, NULL, 10), }; #define VIN_UNIT_ATTRS(X) \ &in_input[X].dev_attr.attr, \ &in_status[X].dev_attr.attr, \ &in_min[X].dev_attr.attr, \ &in_max[X].dev_attr.attr, \ &in_min_alarm[X].dev_attr.attr, \ &in_max_alarm[X].dev_attr.attr static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) { struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm)); } static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) { struct pc87360_data *data = dev_get_drvdata(dev); return sprintf(buf, "%u\n", data->vrm); } static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct pc87360_data *data = dev_get_drvdata(dev); unsigned long val; int err; err = kstrtoul(buf, 10, &val); if (err) return err; if (val > 255) return -EINVAL; data->vrm = val; return count; } static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); static ssize_t show_in_alarms(struct device *dev, struct device_attribute *attr, char *buf) { struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", data->in_alarms); } static DEVICE_ATTR(alarms_in, S_IRUGO, show_in_alarms, NULL); static struct attribute *pc8736x_vin_attr_array[] = { VIN_UNIT_ATTRS(0), VIN_UNIT_ATTRS(1), VIN_UNIT_ATTRS(2), VIN_UNIT_ATTRS(3), VIN_UNIT_ATTRS(4), VIN_UNIT_ATTRS(5), VIN_UNIT_ATTRS(6), VIN_UNIT_ATTRS(7), VIN_UNIT_ATTRS(8), VIN_UNIT_ATTRS(9), VIN_UNIT_ATTRS(10), &dev_attr_cpu0_vid.attr, &dev_attr_vrm.attr, &dev_attr_alarms_in.attr, NULL }; static const struct attribute_group pc8736x_vin_group = { .attrs = pc8736x_vin_attr_array, }; static ssize_t show_therm_input(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index], data->in_vref)); } static ssize_t show_therm_min(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], data->in_vref)); } static ssize_t show_therm_max(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], data->in_vref)); } static ssize_t show_therm_crit(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[attr->index-11], data->in_vref)); } static ssize_t show_therm_status(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", data->in_status[attr->index]); } static ssize_t set_therm_min(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); long val; int err; err = kstrtol(buf, 10, &val); if (err) return err; mutex_lock(&data->update_lock); data->in_min[attr->index] = IN_TO_REG(val, data->in_vref); pc87360_write_value(data, LD_IN, attr->index,
/*
 * MPC512x PSC in SPI mode driver.
 *
 * Copyright (C) 2007,2008 Freescale Semiconductor Inc.
 * Original port from 52xx driver:
 *	Hongjun Chen <hong-jun.chen@freescale.com>
 *
 * Fork of mpc52xx_psc_spi.c:
 *	Copyright (C) 2006 TOPTICA Photonics AG., Dragos Carp
 *
 * This program is free software; you can redistribute  it and/or modify it
 * under  the terms of  the GNU General  Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/completion.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/spi/spi.h>
#include <linux/fsl_devices.h>
#include <linux/gpio.h>
#include <asm/mpc52xx_psc.h>

struct mpc512x_psc_spi {
	void (*cs_control)(struct spi_device *spi, bool on);

	/* driver internal data */
	struct mpc52xx_psc __iomem *psc;
	struct mpc512x_psc_fifo __iomem *fifo;
	unsigned int irq;
	u8 bits_per_word;
	struct clk *clk_mclk;
	struct clk *clk_ipg;
	u32 mclk_rate;

	struct completion txisrdone;
};

/* controller state */
struct mpc512x_psc_spi_cs {
	int bits_per_word;
	int speed_hz;
};

/* set clock freq, clock ramp, bits per work
 * if t is NULL then reset the values to the default values
 */
static int mpc512x_psc_spi_transfer_setup(struct spi_device *spi,
					  struct spi_transfer *t)
{
	struct mpc512x_psc_spi_cs *cs = spi->controller_state;

	cs->speed_hz = (t && t->speed_hz)
	    ? t->speed_hz : spi->max_speed_hz;
	cs->bits_per_word = (t && t->bits_per_word)
	    ? t->bits_per_word : spi->bits_per_word;
	cs->bits_per_word = ((cs->bits_per_word + 7) / 8) * 8;
	return 0;
}

static void mpc512x_psc_spi_activate_cs(struct spi_device *spi)
{
	struct mpc512x_psc_spi_cs *cs = spi->controller_state;
	struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master);
	struct mpc52xx_psc __iomem *psc = mps->psc;
	u32 sicr;
	u32 ccr;
	int speed;
	u16 bclkdiv;

	sicr = in_be32(&psc->sicr);

	/* Set clock phase and polarity */
	if (spi->mode & SPI_CPHA)
		sicr |= 0x00001000;
	else
		sicr &= ~0x00001000;

	if (spi->mode & SPI_CPOL)
		sicr |= 0x00002000;
	else
		sicr &= ~0x00002000;

	if (spi->mode & SPI_LSB_FIRST)
		sicr |= 0x10000000;
	else
		sicr &= ~0x10000000;
	out_be32(&psc->sicr, sicr);

	ccr = in_be32(&psc->ccr);
	ccr &= 0xFF000000;
	speed = cs->speed_hz;
	if (!speed)
		speed = 1000000;	/* default 1MHz */
	bclkdiv = (mps->mclk_rate / speed) - 1;

	ccr |= (((bclkdiv & 0xff) << 16) | (((bclkdiv >> 8) & 0xff) << 8));
	out_be32(&psc->ccr, ccr);
	mps->bits_per_word = cs->bits_per_word;

	if (mps->cs_control && gpio_is_valid(spi->cs_gpio))
		mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 1 : 0);
}

static void mpc512x_psc_spi_deactivate_cs(struct spi_device *spi)
{
	struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master);

	if (mps->cs_control && gpio_is_valid(spi->cs_gpio))
		mps->cs_control(spi, (spi->mode & SPI_CS_HIGH) ? 0 : 1);

}

/* extract and scale size field in txsz or rxsz */
#define MPC512x_PSC_FIFO_SZ(sz) ((sz & 0x7ff) << 2);

#define EOFBYTE 1

static int mpc512x_psc_spi_transfer_rxtx(struct spi_device *spi,
					 struct spi_transfer *t)
{
	struct mpc512x_psc_spi *mps = spi_master_get_devdata(spi->master);
	struct mpc512x_psc_fifo __iomem *fifo = mps->fifo;
	size_t tx_len = t->len;
	size_t rx_len = t->len;
	u8 *tx_buf = (u8 *)t->tx_buf;
	u8 *rx_buf = (u8 *)t->rx_buf;

	if (!tx_buf && !rx_buf && t->len)
		return -EINVAL;

	while (rx_len || tx_len) {
		size_t txcount;
		u8 data;
		size_t fifosz;
		size_t rxcount;
		int rxtries;

		/*
		 * send the TX bytes in as large a chunk as possible
		 * but neither exceed the TX nor the RX FIFOs
		 */
		fifosz = MPC512x_PSC_FIFO_SZ(in_be32(&fifo->txsz));
		txcount = min(fifosz, tx_len);
		fifosz = MPC512x_PSC_FIFO_SZ(in_be32(&fifo->rxsz));
		fifosz -= in_be32(&fifo->rxcnt) + 1;
		txcount = min(fifosz, txcount);
		if (txcount) {

			/* fill the TX FIFO */
			while (txcount-- > 0) {
				data = tx_buf ? *tx_buf++ : 0;
				if (tx_len == EOFBYTE && t->cs_change)
					setbits32(&fifo->txcmd,
						  MPC512x_PSC_FIFO_EOF);
				out_8(&fifo->txdata_8, data);
				tx_len--;
			}

			/* have the ISR trigger when the TX FIFO is empty */
			reinit_completion(&mps->txisrdone);
			out_be32(&fifo->txisr, MPC512x_PSC_FIFO_EMPTY);
			out_be32(&fifo->tximr, MPC512x_PSC_FIFO_EMPTY);
			wait_for_completion(&mps->txisrdone);
		}

		/*
		 * consume as much RX data as the FIFO holds, while we
		 * iterate over the transfer's TX data length
		 *
		 * only insist in draining all the remaining RX bytes
		 * when the TX bytes were exhausted (that's at the very
		 * end of this transfer, not when still iterating over
		 * the t