diff options
Diffstat (limited to 'drivers')
118 files changed, 8795 insertions, 1804 deletions
diff --git a/drivers/block/viodasd.c b/drivers/block/viodasd.c index f63e07bd9f9..b0df4f5ab97 100644 --- a/drivers/block/viodasd.c +++ b/drivers/block/viodasd.c @@ -747,7 +747,7 @@ static int viodasd_remove(struct vio_dev *vdev) * support. */ static struct vio_device_id viodasd_device_table[] __devinitdata = { - { "viodasd", "" }, + { "block", "IBM,iSeries-viodasd" }, { "", "" } }; MODULE_DEVICE_TABLE(vio, viodasd_device_table); diff --git a/drivers/cdrom/viocd.c b/drivers/cdrom/viocd.c index c0f817ba7ad..af6b3bfd169 100644 --- a/drivers/cdrom/viocd.c +++ b/drivers/cdrom/viocd.c @@ -731,7 +731,7 @@ static int viocd_remove(struct vio_dev *vdev) * support. */ static struct vio_device_id viocd_device_table[] __devinitdata = { - { "viocd", "" }, + { "block", "IBM,iSeries-viocd" }, { "", "" } }; MODULE_DEVICE_TABLE(vio, viocd_device_table); diff --git a/drivers/char/agp/Kconfig b/drivers/char/agp/Kconfig index 7c88c060a9e..46685a54077 100644 --- a/drivers/char/agp/Kconfig +++ b/drivers/char/agp/Kconfig @@ -1,7 +1,6 @@ config AGP - tristate "/dev/agpgart (AGP Support)" if !GART_IOMMU + tristate "/dev/agpgart (AGP Support)" depends on ALPHA || IA64 || PPC || X86 - default y if GART_IOMMU ---help--- AGP (Accelerated Graphics Port) is a bus system mainly used to connect graphics cards to the rest of the system. diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 2b6a56b2bf3..a5c6a9d7ff0 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -553,7 +553,6 @@ static int hvc_chars_in_buffer(struct tty_struct *tty) #define HVC_POLL_READ 0x00000001 #define HVC_POLL_WRITE 0x00000002 -#define HVC_POLL_QUICK 0x00000004 static int hvc_poll(struct hvc_struct *hp) { @@ -568,6 +567,7 @@ static int hvc_poll(struct hvc_struct *hp) /* Push pending writes */ if (hp->n_outbuf > 0) hvc_push(hp); + /* Reschedule us if still some write pending */ if (hp->n_outbuf > 0) poll_mask |= HVC_POLL_WRITE; @@ -680,7 +680,7 @@ int khvcd(void *unused) poll_mask |= HVC_POLL_READ; if (hvc_kicked) continue; - if (poll_mask & HVC_POLL_QUICK) { + if (poll_mask & HVC_POLL_WRITE) { yield(); continue; } diff --git a/drivers/char/hvc_rtas.c b/drivers/char/hvc_rtas.c index 83364ea63cb..57106e02fd2 100644 --- a/drivers/char/hvc_rtas.c +++ b/drivers/char/hvc_rtas.c @@ -41,37 +41,28 @@ #define hvc_rtas_cookie 0x67781e15 struct hvc_struct *hvc_rtas_dev; -#define RTASCONS_PUT_ATTEMPTS 16 - static int rtascons_put_char_token = RTAS_UNKNOWN_SERVICE; static int rtascons_get_char_token = RTAS_UNKNOWN_SERVICE; -static int rtascons_put_delay = 100; -module_param_named(put_delay, rtascons_put_delay, int, 0644); -static inline int hvc_rtas_write_console(uint32_t vtermno, const char *buf, int count) +static inline int hvc_rtas_write_console(uint32_t vtermno, const char *buf, + int count) { - int done; + int i; - /* if there is more than one character to be displayed, wait a bit */ - for (done = 0; done < count; done++) { - int result; - result = rtas_call(rtascons_put_char_token, 1, 1, NULL, buf[done]); - if (result) + for (i = 0; i < count; i++) { + if (rtas_call(rtascons_put_char_token, 1, 1, NULL, buf[i])) break; } - /* the calling routine expects to receive the number of bytes sent */ - return done; + + return i; } static int hvc_rtas_read_console(uint32_t vtermno, char *buf, int count) { - int i; + int i, c; for (i = 0; i < count; i++) { - int c, err; - - err = rtas_call(rtascons_get_char_token, 0, 2, &c); - if (err) + if (rtas_call(rtascons_get_char_token, 0, 2, &c)) break; buf[i] = c; @@ -106,7 +97,9 @@ static int hvc_rtas_init(void) hp = hvc_alloc(hvc_rtas_cookie, NO_IRQ, &hvc_rtas_get_put_ops); if (IS_ERR(hp)) return PTR_ERR(hp); + hvc_rtas_dev = hp; + return 0; } module_init(hvc_rtas_init); @@ -114,8 +107,8 @@ module_init(hvc_rtas_init); /* This will tear down the tty portion of the driver */ static void __exit hvc_rtas_exit(void) { - /* Really the fun isn't over until the worker thread breaks down and the - * tty cleans up */ + /* Really the fun isn't over until the worker thread breaks down and + * the tty cleans up */ if (hvc_rtas_dev) hvc_remove(hvc_rtas_dev); } @@ -127,12 +120,14 @@ static int hvc_rtas_console_init(void) rtascons_put_char_token = rtas_token("put-term-char"); if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE) return -EIO; + rtascons_get_char_token = rtas_token("get-term-char"); if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE) return -EIO; - hvc_instantiate(hvc_rtas_cookie, 0, &hvc_rtas_get_put_ops ); + hvc_instantiate(hvc_rtas_cookie, 0, &hvc_rtas_get_put_ops); add_preferred_console("hvc", 0, NULL); + return 0; } console_initcall(hvc_rtas_console_init); diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c index a9522189fc9..a0370ed752c 100644 --- a/drivers/char/hvsi.c +++ b/drivers/char/hvsi.c @@ -1179,7 +1179,7 @@ static int __init hvsi_init(void) if (tty_register_driver(hvsi_driver)) panic("Couldn't register hvsi console driver\n"); - printk(KERN_INFO "HVSI: registered %i devices\n", hvsi_count); + printk(KERN_DEBUG "HVSI: registered %i devices\n", hvsi_count); return 0; } diff --git a/drivers/char/viotape.c b/drivers/char/viotape.c index 60aabdb4a04..11c7e9de595 100644 --- a/drivers/char/viotape.c +++ b/drivers/char/viotape.c @@ -989,7 +989,7 @@ static int viotape_remove(struct vio_dev *vdev) * support. */ static struct vio_device_id viotape_device_table[] __devinitdata = { - { "viotape", "" }, + { "byte", "IBM,iSeries-viotape" }, { "", "" } }; MODULE_DEVICE_TABLE(vio, viotape_device_table); diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index e07a35487bd..8878a154ed4 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -72,6 +72,14 @@ static DEFINE_PER_CPU(struct cpu_dbs_info_s, cpu_dbs_info); static unsigned int dbs_enable; /* number of CPUs using this policy */ +/* + * DEADLOCK ALERT! There is a ordering requirement between cpu_hotplug + * lock and dbs_mutex. cpu_hotplug lock should always be held before + * dbs_mutex. If any function that can potentially take cpu_hotplug lock + * (like __cpufreq_driver_target()) is being called with dbs_mutex taken, then + * cpu_hotplug lock should be taken before that. Note that cpu_hotplug lock + * is recursive for the same process. -Venki + */ static DEFINE_MUTEX (dbs_mutex); static DECLARE_WORK (dbs_work, do_dbs_timer, NULL); @@ -414,12 +422,14 @@ static void dbs_check_cpu(int cpu) static void do_dbs_timer(void *data) { int i; + lock_cpu_hotplug(); mutex_lock(&dbs_mutex); for_each_online_cpu(i) dbs_check_cpu(i); schedule_delayed_work(&dbs_work, usecs_to_jiffies(dbs_tuners_ins.sampling_rate)); mutex_unlock(&dbs_mutex); + unlock_cpu_hotplug(); } static inline void dbs_timer_init(void) @@ -514,6 +524,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, break; case CPUFREQ_GOV_LIMITS: + lock_cpu_hotplug(); mutex_lock(&dbs_mutex); if (policy->max < this_dbs_info->cur_policy->cur) __cpufreq_driver_target( @@ -524,6 +535,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, this_dbs_info->cur_policy, policy->min, CPUFREQ_RELATION_L); mutex_unlock(&dbs_mutex); + unlock_cpu_hotplug(); break; } return 0; diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 3e6ffcaa5af..4d308410b60 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -71,6 +71,14 @@ static DEFINE_PER_CPU(struct cpu_dbs_info_s, cpu_dbs_info); static unsigned int dbs_enable; /* number of CPUs using this policy */ +/* + * DEADLOCK ALERT! There is a ordering requirement between cpu_hotplug + * lock and dbs_mutex. cpu_hotplug lock should always be held before + * dbs_mutex. If any function that can potentially take cpu_hotplug lock + * (like __cpufreq_driver_target()) is being called with dbs_mutex taken, then + * cpu_hotplug lock should be taken before that. Note that cpu_hotplug lock + * is recursive for the same process. -Venki + */ static DEFINE_MUTEX (dbs_mutex); static DECLARE_WORK (dbs_work, do_dbs_timer, NULL); @@ -363,12 +371,14 @@ static void dbs_check_cpu(int cpu) static void do_dbs_timer(void *data) { int i; + lock_cpu_hotplug(); mutex_lock(&dbs_mutex); for_each_online_cpu(i) dbs_check_cpu(i); queue_delayed_work(dbs_workq, &dbs_work, usecs_to_jiffies(dbs_tuners_ins.sampling_rate)); mutex_unlock(&dbs_mutex); + unlock_cpu_hotplug(); } static inline void dbs_timer_init(void) @@ -469,6 +479,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, break; case CPUFREQ_GOV_LIMITS: + lock_cpu_hotplug(); mutex_lock(&dbs_mutex); if (policy->max < this_dbs_info->cur_policy->cur) __cpufreq_driver_target( @@ -479,6 +490,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, this_dbs_info->cur_policy, policy->min, CPUFREQ_RELATION_L); mutex_unlock(&dbs_mutex); + unlock_cpu_hotplug(); break; } return 0; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 99cdc612d2c..0e31a0c496e 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1,5 +1,5 @@ # -# I2C Sensor chip drivers configuration +# Hardware monitoring chip drivers configuration # menu "Hardware Monitoring support" @@ -16,6 +16,10 @@ config HWMON should say Y here and also to the specific driver(s) for your sensors chip(s) below. + To find out which specific driver(s) you need, use the + sensors-detect script from the lm_sensors package. Read + <file:Documentation/hwmon/userspace-tools> for details. + This support can also be built as a module. If so, the module will be called hwmon. @@ -23,6 +27,18 @@ config HWMON_VID tristate default n +config SENSORS_ABITUGURU + tristate "Abit uGuru" + depends on HWMON && EXPERIMENTAL + help + If you say yes here you get support for the Abit uGuru chips + sensor part. The voltage and frequency control parts of the Abit + uGuru are not supported. The Abit uGuru chip can be found on Abit + uGuru featuring motherboards (most modern Abit motherboards). + + This driver can also be built as a module. If so, the module + will be called abituguru. + config SENSORS_ADM1021 tristate "Analog Devices ADM1021 and compatibles" depends on HWMON && I2C @@ -188,6 +204,16 @@ config SENSORS_LM63 This driver can also be built as a module. If so, the module will be called lm63. +config SENSORS_LM70 + tristate "National Semiconductor LM70" + depends on HWMON && SPI_MASTER && EXPERIMENTAL + help + If you say yes here you get support for the National Semiconductor + LM70 digital temperature sensor chip. + + This driver can also be built as a module. If so, the module + will be called lm70. + config SENSORS_LM75 tristate "National Semiconductor LM75 and compatibles" depends on HWMON && I2C @@ -236,11 +262,11 @@ config SENSORS_LM80 will be called lm80. config SENSORS_LM83 - tristate "National Semiconductor LM83" + tristate "National Semiconductor LM83 and compatibles" depends on HWMON && I2C help If you say yes here you get support for National Semiconductor - LM83 sensor chips. + LM82 and LM83 sensor chips. This driver can also be built as a module. If so, the module will be called lm83. @@ -333,11 +359,32 @@ config SENSORS_SMSC47M1 help If you say yes here you get support for the integrated fan monitoring and control capabilities of the SMSC LPC47B27x, - LPC47M10x, LPC47M13x, LPC47M14x, LPC47M15x and LPC47M192 chips. + LPC47M10x, LPC47M13x, LPC47M14x, LPC47M15x, LPC47M192 and + LPC47M997 chips. + + The temperature and voltage sensor features of the LPC47M192 + and LPC47M997 are supported by another driver, select also + "SMSC LPC47M192 and compatibles" below for those. This driver can also be built as a module. If so, the module will be called smsc47m1. +config SENSORS_SMSC47M192 + tristate "SMSC LPC47M192 and compatibles" + depends on HWMON && I2C && EXPERIMENTAL + select HWMON_VID + help + If you say yes here you get support for the temperature and + voltage sensors of the SMSC LPC47M192 and LPC47M997 chips. + + The fan monitoring and control capabilities of these chips + are supported by another driver, select + "SMSC LPC47M10x and compatibles" above. You need both drivers + if you want fan control and voltage/temperature sensor support. + + This driver can also be built as a module. If so, the module + will be called smsc47m192. + config SENSORS_SMSC47B397 tristate "SMSC LPC47B397-NC" depends on HWMON && I2C && EXPERIMENTAL @@ -385,6 +432,16 @@ config SENSORS_W83781D This driver can also be built as a module. If so, the module will be called w83781d. +config SENSORS_W83791D + tristate "Winbond W83791D" + depends on HWMON && I2C && EXPERIMENTAL + select HWMON_VID + help + If you say yes here you get support for the Winbond W83791D chip. + + This driver can also be built as a module. If so, the module + will be called w83791d. + config SENSORS_W83792D tristate "Winbond W83792D" depends on HWMON && I2C && EXPERIMENTAL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index fbdb8d911a7..31415843a91 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -10,7 +10,9 @@ obj-$(CONFIG_SENSORS_ASB100) += asb100.o obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o obj-$(CONFIG_SENSORS_W83792D) += w83792d.o obj-$(CONFIG_SENSORS_W83781D) += w83781d.o +obj-$(CONFIG_SENSORS_W83791D) += w83791d.o +obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o @@ -26,6 +28,7 @@ obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_LM63) += lm63.o +obj-$(CONFIG_SENSORS_LM70) += lm70.o obj-$(CONFIG_SENSORS_LM75) += lm75.o obj-$(CONFIG_SENSORS_LM77) += lm77.o obj-$(CONFIG_SENSORS_LM78) += lm78.o @@ -40,6 +43,7 @@ obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o +obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o obj-$(CONFIG_SENSORS_VT8231) += vt8231.o obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c new file mode 100644 index 00000000000..59122cc0a50 --- /dev/null +++ b/drivers/hwmon/abituguru.c @@ -0,0 +1,1415 @@ +/* + abituguru.c Copyright (c) 2005-2006 Hans de Goede <j.w.r.degoede@hhs.nl> + + 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. +*/ +/* + This driver supports the sensor part of the custom Abit uGuru chip found + on Abit uGuru motherboards. Note: because of lack of specs the CPU / RAM / + etc voltage & frequency control is not supported! +*/ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/mutex.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <asm/io.h> + +/* Banks */ +#define ABIT_UGURU_ALARM_BANK 0x20 /* 1x 3 bytes */ +#define ABIT_UGURU_SENSOR_BANK1 0x21 /* 16x volt and temp */ +#define ABIT_UGURU_FAN_PWM 0x24 /* 3x 5 bytes */ +#define ABIT_UGURU_SENSOR_BANK2 0x26 /* fans */ +/* max nr of sensors in bank1, a bank1 sensor can be in, temp or nc */ +#define ABIT_UGURU_MAX_BANK1_SENSORS 16 +/* Warning if you increase one of the 2 MAX defines below to 10 or higher you + should adjust the belonging _NAMES_LENGTH macro for the 2 digit number! */ +/* max nr of sensors in bank2, currently mb's with max 6 fans are known */ +#define ABIT_UGURU_MAX_BANK2_SENSORS 6 +/* max nr of pwm outputs, currently mb's with max 5 pwm outputs are known */ +#define ABIT_UGURU_MAX_PWMS 5 +/* uGuru sensor bank 1 flags */ /* Alarm if: */ +#define ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE 0x01 /* temp over warn */ +#define ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE 0x02 /* volt over max */ +#define ABIT_UGURU_VOLT_LOW_ALARM_ENABLE 0x04 /* volt under min */ +#define ABIT_UGURU_TEMP_HIGH_ALARM_FLAG 0x10 /* temp is over warn */ +#define ABIT_UGURU_VOLT_HIGH_ALARM_FLAG 0x20 /* volt is over max */ +#define ABIT_UGURU_VOLT_LOW_ALARM_FLAG 0x40 /* volt is under min */ +/* uGuru sensor bank 2 flags */ /* Alarm if: */ +#define ABIT_UGURU_FAN_LOW_ALARM_ENABLE 0x01 /* fan under min */ +/* uGuru sensor bank common flags */ +#define ABIT_UGURU_BEEP_ENABLE 0x08 /* beep if alarm */ +#define ABIT_UGURU_SHUTDOWN_ENABLE 0x80 /* shutdown if alarm */ +/* uGuru fan PWM (speed control) flags */ +#define ABIT_UGURU_FAN_PWM_ENABLE 0x80 /* enable speed control */ +/* Values used for conversion */ +#define ABIT_UGURU_FAN_MAX 15300 /* RPM */ +/* Bank1 sensor types */ +#define ABIT_UGURU_IN_SENSOR 0 +#define ABIT_UGURU_TEMP_SENSOR 1 +#define ABIT_UGURU_NC 2 +/* Timeouts / Retries, if these turn out to need a lot of fiddling we could + convert them to params. */ +/* 250 was determined by trial and error, 200 works most of the time, but not + always. I assume this is cpu-speed independent, since the ISA-bus and not + the CPU should be the bottleneck. Note that 250 sometimes is still not + enough (only reported on AN7 mb) this is handled by a higher layer. */ +#define ABIT_UGURU_WAIT_TIMEOUT 250 +/* Normally all expected status in abituguru_ready, are reported after the + first read, but sometimes not and we need to poll, 5 polls was not enough + 50 sofar is. */ +#define ABIT_UGURU_READY_TIMEOUT 50 +/* Maximum 3 retries on timedout reads/writes, delay 200 ms before retrying */ +#define ABIT_UGURU_MAX_RETRIES 3 +#define ABIT_UGURU_RETRY_DELAY (HZ/5) +/* Maximum 2 timeouts in abituguru_update_device, iow 3 in a row is an error */ +#define ABIT_UGURU_MAX_TIMEOUTS 2 +/* utility macros */ +#define ABIT_UGURU_NAME "abituguru" +#define ABIT_UGURU_DEBUG(level, format, arg...) \ + if (level <= verbose) \ + printk(KERN_DEBUG ABIT_UGURU_NAME ": " format , ## arg) +/* Macros to help calculate the sysfs_names array length */ +/* sum of strlen of: in??_input\0, in??_{min,max}\0, in??_{min,max}_alarm\0, + in??_{min,max}_alarm_enable\0, in??_beep\0, in??_shutdown\0 */ +#define ABITUGURU_IN_NAMES_LENGTH (11 + 2 * 9 + 2 * 15 + 2 * 22 + 10 + 14) +/* sum of strlen of: temp??_input\0, temp??_max\0, temp??_crit\0, + temp??_alarm\0, temp??_alarm_enable\0, temp??_beep\0, temp??_shutdown\0 */ +#define ABITUGURU_TEMP_NAMES_LENGTH (13 + 11 + 12 + 13 + 20 + 12 + 16) +/* sum of strlen of: fan?_input\0, fan?_min\0, fan?_alarm\0, + fan?_alarm_enable\0, fan?_beep\0, fan?_shutdown\0 */ +#define ABITUGURU_FAN_NAMES_LENGTH (11 + 9 + 11 + 18 + 10 + 14) +/* sum of strlen of: pwm?_enable\0, pwm?_auto_channels_temp\0, + pwm?_auto_point{1,2}_pwm\0, pwm?_auto_point{1,2}_temp\0 */ +#define ABITUGURU_PWM_NAMES_LENGTH (12 + 24 + 2 * 21 + 2 * 22) +/* IN_NAMES_LENGTH > TEMP_NAMES_LENGTH so assume all bank1 sensors are in */ +#define ABITUGURU_SYSFS_NAMES_LENGTH ( \ + ABIT_UGURU_MAX_BANK1_SENSORS * ABITUGURU_IN_NAMES_LENGTH + \ + ABIT_UGURU_MAX_BANK2_SENSORS * ABITUGURU_FAN_NAMES_LENGTH + \ + ABIT_UGURU_MAX_PWMS * ABITUGURU_PWM_NAMES_LENGTH) + +/* All the macros below are named identical to the oguru and oguru2 programs + reverse engineered by Olle Sandberg, hence the names might not be 100% + logical. I could come up with better names, but I prefer keeping the names + identical so that this driver can be compared with his work more easily. */ +/* Two i/o-ports are used by uGuru */ +#define ABIT_UGURU_BASE 0x00E0 +/* Used to tell uGuru what to read and to read the actual data */ +#define ABIT_UGURU_CMD 0x00 +/* Mostly used to check if uGuru is busy */ +#define ABIT_UGURU_DATA 0x04 +#define ABIT_UGURU_REGION_LENGTH 5 +/* uGuru status' */ +#define ABIT_UGURU_STATUS_WRITE 0x00 /* Ready to be written */ +#define ABIT_UGURU_STATUS_READ 0x01 /* Ready to be read */ +#define ABIT_UGURU_STATUS_INPUT 0x08 /* More input */ +#define ABIT_UGURU_STATUS_READY 0x09 /* Ready to be written */ + +/* Constants */ +/* in (Volt) sensors go up to 3494 mV, temp to 255000 millidegrees Celsius */ +static const int abituguru_bank1_max_value[2] = { 3494, 255000 }; +/* Min / Max allowed values for sensor2 (fan) alarm threshold, these values + correspond to 300-3000 RPM */ +static const u8 abituguru_bank2_min_threshold = 5; +static const u8 abituguru_bank2_max_threshold = 50; +/* Register 0 is a bitfield, 1 and 2 are pwm settings (255 = 100%), 3 and 4 + are temperature trip points. */ +static const int abituguru_pwm_settings_multiplier[5] = { 0, 1, 1, 1000, 1000 }; +/* Min / Max allowed values for pwm_settings. Note: pwm1 (CPU fan) is a + special case the minium allowed pwm% setting for this is 30% (77) on + some MB's this special case is handled in the code! */ +static const u8 abituguru_pwm_min[5] = { 0, 170, 170, 25, 25 }; +static const u8 abituguru_pwm_max[5] = { 0, 255, 255, 75, 75 }; + + +/* Insmod parameters */ +static int force; +module_param(force, bool, 0); +MODULE_PARM_DESC(force, "Set to one to force detection."); +static int fan_sensors; +module_param(fan_sensors, int, 0); +MODULE_PARM_DESC(fan_sensors, "Number of fan sensors on the uGuru " + "(0 = autodetect)"); +static int pwms; +module_param(pwms, int, 0); +MODULE_PARM_DESC(pwms, "Number of PWMs on the uGuru " + "(0 = autodetect)"); + +/* Default verbose is 2, since this driver is still in the testing phase */ +static int verbose = 2; +module_param(verbose, int, 0644); +MODULE_PARM_DESC(verbose, "How verbose should the driver be? (0-3):\n" + " 0 normal output\n" + " 1 + verbose error reporting\n" + " 2 + sensors type probing info\n" + " 3 + retryable error reporting"); + + +/* For the Abit uGuru, we need to keep some data in memory. + The structure is dynamically allocated, at the same time when a new + abituguru device is allocated. */ +struct abituguru_data { + struct class_device *class_dev; /* hwmon registered device */ + struct mutex update_lock; /* protect access to data and uGuru */ + unsigned long last_updated; /* In jiffies */ + unsigned short addr; /* uguru base address */ + char uguru_ready; /* is the uguru in ready state? */ + unsigned char update_timeouts; /* number of update timeouts since last + successful update */ + + /* The sysfs attr and their names are generated automatically, for bank1 + we cannot use a predefined array because we don't know beforehand + of a sensor is a volt or a temp sensor, for bank2 and the pwms its + easier todo things the same way. For in sensors we have 9 (temp 7) + sysfs entries per sensor, for bank2 and pwms 6. */ + struct sensor_device_attribute_2 sysfs_attr[ + ABIT_UGURU_MAX_BANK1_SENSORS * 9 + + ABIT_UGURU_MAX_BANK2_SENSORS * 6 + ABIT_UGURU_MAX_PWMS * 6]; + /* Buffer to store the dynamically generated sysfs names */ + char sysfs_names[ABITUGURU_SYSFS_NAMES_LENGTH]; + + /* Bank 1 data */ + /* number of and addresses of [0] in, [1] temp sensors */ + u8 bank1_sensors[2]; + u8 bank1_address[2][ABIT_UGURU_MAX_BANK1_SENSORS]; + u8 bank1_value[ABIT_UGURU_MAX_BANK1_SENSORS]; + /* This array holds 3 entries per sensor for the bank 1 sensor settings + (flags, min, max for voltage / flags, warn, shutdown for temp). */ + u8 bank1_settings[ABIT_UGURU_MAX_BANK1_SENSORS][3]; + /* Maximum value for each sensor used for scaling in mV/millidegrees + Celsius. */ + int bank1_max_value[ABIT_UGURU_MAX_BANK1_SENSORS]; + + /* Bank 2 data, ABIT_UGURU_MAX_BANK2_SENSORS entries for bank2 */ + u8 bank2_sensors; /* actual number of bank2 sensors found */ + u8 bank2_value[ABIT_UGURU_MAX_BANK2_SENSORS]; + u8 bank2_settings[ABIT_UGURU_MAX_BANK2_SENSORS][2]; /* flags, min */ + + /* Alarms 2 bytes for bank1, 1 byte for bank2 */ + u8 alarms[3]; + + /* Fan PWM (speed control) 5 bytes per PWM */ + u8 pwms; /* actual number of pwms found */ + u8 pwm_settings[ABIT_UGURU_MAX_PWMS][5]; +}; + +/* wait till the uguru is in the specified state */ +static int abituguru_wait(struct abituguru_data *data, u8 state) +{ + int timeout = ABIT_UGURU_WAIT_TIMEOUT; + + while (inb_p(data->addr + ABIT_UGURU_DATA) != state) { + timeout--; + if (timeout == 0) + return -EBUSY; + } + return 0; +} + +/* Put the uguru in ready for input state */ +static int abituguru_ready(struct abituguru_data *data) +{ + int timeout = ABIT_UGURU_READY_TIMEOUT; + + if (data->uguru_ready) + return 0; + + /* Reset? / Prepare for next read/write cycle */ + outb(0x00, data->addr + ABIT_UGURU_DATA); + + /* Wait till the uguru is ready */ + if (abituguru_wait(data, ABIT_UGURU_STATUS_READY)) { + ABIT_UGURU_DEBUG(1, + "timeout exceeded waiting for ready state\n"); + return -EIO; + } + + /* Cmd port MUST be read now and should contain 0xAC */ + while (inb_p(data->addr + ABIT_UGURU_CMD) != 0xAC) { + timeout--; + if (timeout == 0) { + ABIT_UGURU_DEBUG(1, + "CMD reg does not hold 0xAC after ready command\n"); + return -EIO; + } + } + + /* After this the ABIT_UGURU_DATA port should contain + ABIT_UGURU_STATUS_INPUT */ + timeout = ABIT_UGURU_READY_TIMEOUT; + while (inb_p(data->addr + ABIT_UGURU_DATA) != ABIT_UGURU_STATUS_INPUT) { + timeout--; + if (timeout == 0) { + ABIT_UGURU_DEBUG(1, + "state != more input after ready command\n"); + return -EIO; + } + } + + data->uguru_ready = 1; + return 0; +} + +/* Send the bank and then sensor address to the uGuru for the next read/write + cycle. This function gets called as the first part of a read/write by + abituguru_read and abituguru_write. This function should never be + called by any other function. */ +static int abituguru_send_address(struct abituguru_data *data, + u8 bank_addr, u8 sensor_addr, int retries) +{ + /* assume the caller does error handling itself if it has not requested + any retries, and thus be quiet. */ + int report_errors = retries; + + for (;;) { + /* Make sure the uguru is ready and then send the bank address, + after this the uguru is no longer "ready". */ + if (abituguru_ready(data) != 0) + return -EIO; + outb(bank_addr, data->addr + ABIT_UGURU_DATA); + data->uguru_ready = 0; + + /* Wait till the uguru is ABIT_UGURU_STATUS_INPUT state again + and send the sensor addr */ + if (abituguru_wait(data, ABIT_UGURU_STATUS_INPUT)) { + if (retries) { + ABIT_UGURU_DEBUG(3, "timeout exceeded " + "waiting for more input state, %d " + "tries remaining\n", retries); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(ABIT_UGURU_RETRY_DELAY); + retries--; + continue; + } + if (report_errors) + ABIT_UGURU_DEBUG(1, "timeout exceeded " + "waiting for more input state " + "(bank: %d)\n", (int)bank_addr); + return -EBUSY; + } + outb(sensor_addr, data->addr + ABIT_UGURU_CMD); + return 0; + } +} + +/* Read count bytes from sensor sensor_addr in bank bank_addr and store the + result in buf, retry the send address part of the read retries times. */ +static int abituguru_read(struct abituguru_data *data, + u8 bank_addr, u8 sensor_addr, u8 *buf, int count, int retries) +{ + int i; + + /* Send the address */ + i = abituguru_send_address(data, bank_addr, sensor_addr, retries); + if (i) + return i; + + /* And read the data */ + for (i = 0; i < count; i++) { + if (abituguru_wait(data, ABIT_UGURU_STATUS_READ)) { + ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for " + "read state (bank: %d, sensor: %d)\n", + (int)bank_addr, (int)sensor_addr); + break; + } + buf[i] = inb(data->addr + ABIT_UGURU_CMD); + } + + /* Last put the chip back in ready state */ + abituguru_ready(data); + + return i; +} + +/* Write count bytes from buf to sensor sensor_addr in bank bank_addr, the send + address part of the write is always retried ABIT_UGURU_MAX_RETRIES times. */ +static int abituguru_write(struct abituguru_data *data, + u8 bank_addr, u8 sensor_addr, u8 *buf, int count) +{ + int i; + + /* Send the address */ + i = abituguru_send_address(data, bank_addr, sensor_addr, + ABIT_UGURU_MAX_RETRIES); + if (i) + return i; + + /* And write the data */ + for (i = 0; i < count; i++) { + if (abituguru_wait(data, ABIT_UGURU_STATUS_WRITE)) { + ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for " + "write state (bank: %d, sensor: %d)\n", + (int)bank_addr, (int)sensor_addr); + break; + } + outb(buf[i], data->addr + ABIT_UGURU_CMD); + } + + /* Now we need to wait till the chip is ready to be read again, + don't ask why */ + if (abituguru_wait(data, ABIT_UGURU_STATUS_READ)) { + ABIT_UGURU_DEBUG(1, "timeout exceeded waiting for read state " + "after write (bank: %d, sensor: %d)\n", (int)bank_addr, + (int)sensor_addr); + return -EIO; + } + + /* Cmd port MUST be read now and should contain 0xAC */ + if (inb_p(data->addr + ABIT_UGURU_CMD) != 0xAC) { + ABIT_UGURU_DEBUG(1, "CMD reg does not hold 0xAC after write " + "(bank: %d, sensor: %d)\n", (int)bank_addr, + (int)sensor_addr); + return -EIO; + } + + /* Last put the chip back in ready state */ + abituguru_ready(data); + + return i; +} + +/* Detect sensor type. Temp and Volt sensors are enabled with + different masks and will ignore enable masks not meant for them. + This enables us to test what kind of sensor we're dealing with. + By setting the alarm thresholds so that we will always get an + alarm for sensor type X and then enabling the sensor as sensor type + X, if we then get an alarm it is a sensor of type X. */ +static int __devinit +abituguru_detect_bank1_sensor_type(struct abituguru_data *data, + u8 sensor_addr) +{ + u8 val, buf[3]; + int ret = ABIT_UGURU_NC; + + /* First read the sensor and the current settings */ + if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, sensor_addr, &val, + 1, ABIT_UGURU_MAX_RETRIES) != 1) + return -ENODEV; + + /* Test val is sane / usable for sensor type detection. */ + if ((val < 10u) || (val > 240u)) { + printk(KERN_WARNING ABIT_UGURU_NAME + ": bank1-sensor: %d reading (%d) too close to limits, " + "unable to determine sensor type, skipping sensor\n", + (int)sensor_addr, (int)val); + /* assume no sensor is there for sensors for which we can't + determine the sensor type because their reading is too close + to their limits, this usually means no sensor is there. */ + return ABIT_UGURU_NC; + } + + ABIT_UGURU_DEBUG(2, "testing bank1 sensor %d\n", (int)sensor_addr); + /* Volt sensor test, enable volt low alarm, set min value ridicously + high. If its a volt sensor this should always give us an alarm. */ + buf[0] = ABIT_UGURU_VOLT_LOW_ALARM_ENABLE; + buf[1] = 245; + buf[2] = 250; + if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr, + buf, 3) != 3) + return -ENODEV; + /* Now we need 20 ms to give the uguru time to read the sensors + and raise a voltage alarm */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/50); + /* Check for alarm and check the alarm is a volt low alarm. */ + if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, buf, 3, + ABIT_UGURU_MAX_RETRIES) != 3) + return -ENODEV; + if (buf[sensor_addr/8] & (0x01 << (sensor_addr % 8))) { + if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1, + sensor_addr, buf, 3, + ABIT_UGURU_MAX_RETRIES) != 3) + return -ENODEV; + if (buf[0] & ABIT_UGURU_VOLT_LOW_ALARM_FLAG) { + /* Restore original settings */ + if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, + sensor_addr, + data->bank1_settings[sensor_addr], + 3) != 3) + return -ENODEV; + ABIT_UGURU_DEBUG(2, " found volt sensor\n"); + return ABIT_UGURU_IN_SENSOR; + } else + ABIT_UGURU_DEBUG(2, " alarm raised during volt " + "sensor test, but volt low flag not set\n"); + } else + ABIT_UGURU_DEBUG(2, " alarm not raised during volt sensor " + "test\n"); + + /* Temp sensor test, enable sensor as a temp sensor, set beep value + ridicously low (but not too low, otherwise uguru ignores it). + If its a temp sensor this should always give us an alarm. */ + buf[0] = ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE; + buf[1] = 5; + buf[2] = 10; + if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr, + buf, 3) != 3) + return -ENODEV; + /* Now we need 50 ms to give the uguru time to read the sensors + and raise a temp alarm */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/20); + /* Check for alarm and check the alarm is a temp high alarm. */ + if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, buf, 3, + ABIT_UGURU_MAX_RETRIES) != 3) + return -ENODEV; + if (buf[sensor_addr/8] & (0x01 << (sensor_addr % 8))) { + if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1 + 1, + sensor_addr, buf, 3, + ABIT_UGURU_MAX_RETRIES) != 3) + return -ENODEV; + if (buf[0] & ABIT_UGURU_TEMP_HIGH_ALARM_FLAG) { + ret = ABIT_UGURU_TEMP_SENSOR; + ABIT_UGURU_DEBUG(2, " found temp sensor\n"); + } else + ABIT_UGURU_DEBUG(2, " alarm raised during temp " + "sensor test, but temp high flag not set\n"); + } else + ABIT_UGURU_DEBUG(2, " alarm not raised during temp sensor " + "test\n"); + + /* Restore original settings */ + if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, sensor_addr, + data->bank1_settings[sensor_addr], 3) != 3) + return -ENODEV; + + return ret; +} + +/* These functions try to find out how many sensors there are in bank2 and how + many pwms there are. The purpose of this is to make sure that we don't give + the user the possibility to change settings for non-existent sensors / pwm. + The uGuru will happily read / write whatever memory happens to be after the + memory storing the PWM settings when reading/writing to a PWM which is not + there. Notice even if we detect a PWM which doesn't exist we normally won't + write to it, unless the user tries to change the settings. + + Although the uGuru allows reading (settings) from non existing bank2 + sensors, my version of the uGuru does seem to stop writing to them, the + write function above aborts in this case with: + "CMD reg does not hold 0xAC after write" + + Notice these 2 tests are non destructive iow read-only tests, otherwise + they would defeat their purpose. Although for the bank2_sensors detection a + read/write test would be feasible because of the reaction above, I've + however opted to stay on the safe side. */ +static void __devinit +abituguru_detect_no_bank2_sensors(struct abituguru_data *data) +{ + int i; + + if (fan_sensors) { + data->bank2_sensors = fan_sensors; + ABIT_UGURU_DEBUG(2, "assuming %d fan sensors because of " + "\"fan_sensors\" module param\n", + (int)data->bank2_sensors); + return; + } + + ABIT_UGURU_DEBUG(2, "detecting number of fan sensors\n"); + for (i = 0; i < ABIT_UGURU_MAX_BANK2_SENSORS; i++) { + /* 0x89 are the known used bits: + -0x80 enable shutdown + -0x08 enable beep + -0x01 enable alarm + All other bits should be 0, but on some motherboards + 0x40 (bit 6) is also high for some of the fans?? */ + if (data->bank2_settings[i][0] & ~0xC9) { + ABIT_UGURU_DEBUG(2, " bank2 sensor %d does not seem " + "to be a fan sensor: settings[0] = %02X\n", + i, (unsigned int)data->bank2_settings[i][0]); + break; + } + + /* check if the threshold is within the allowed range */ + if (data->bank2_settings[i][1] < + abituguru_bank2_min_threshold) { + ABIT_UGURU_DEBUG(2, " bank2 sensor %d does not seem " + "to be a fan sensor: the threshold (%d) is " + "below the minimum (%d)\n", i, + (int)data->bank2_settings[i][1], + (int)abituguru_bank2_min_threshold); + break; + } + if (data->bank2_settings[i][1] > + abituguru_bank2_max_threshold) { + ABIT_UGURU_DEBUG(2, " bank2 sensor %d does not seem " + "to be a fan sensor: the threshold (%d) is " + "above the maximum (%d)\n", i, + (int)data->bank2_settings[i][1], + (int)abituguru_bank2_max_threshold); + break; + } + } + + data->bank2_sensors = i; + ABIT_UGURU_DEBUG(2, " found: %d fan sensors\n", + (int)data->bank2_sensors); +} + +static void __devinit +abituguru_detect_no_pwms(struct abituguru_data *data) +{ + int i, j; + + if (pwms) { + data->pwms = pwms; + ABIT_UGURU_DEBUG(2, "assuming %d PWM outputs because of " + "\"pwms\" module param\n", (int)data->pwms); + return; + } + + ABIT_UGURU_DEBUG(2, "detecting number of PWM outputs\n"); + for (i = 0; i < ABIT_UGURU_MAX_PWMS; i++) { + /* 0x80 is the enable bit and the low + nibble is which temp sensor to use, + the other bits should be 0 */ + if (data->pwm_settings[i][0] & ~0x8F) { + ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem " + "to be a pwm channel: settings[0] = %02X\n", + i, (unsigned int)data->pwm_settings[i][0]); + break; + } + + /* the low nibble must correspond to one of the temp sensors + we've found */ + for (j = 0; j < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]; + j++) { + if (data->bank1_address[ABIT_UGURU_TEMP_SENSOR][j] == + (data->pwm_settings[i][0] & 0x0F)) + break; + } + if (j == data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]) { + ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem " + "to be a pwm channel: %d is not a valid temp " + "sensor address\n", i, + data->pwm_settings[i][0] & 0x0F); + break; + } + + /* check if all other settings are within the allowed range */ + for (j = 1; j < 5; j++) { + u8 min; + /* special case pwm1 min pwm% */ + if ((i == 0) && ((j == 1) || (j == 2))) + min = 77; + else + min = abituguru_pwm_min[j]; + if (data->pwm_settings[i][j] < min) { + ABIT_UGURU_DEBUG(2, " pwm channel %d does " + "not seem to be a pwm channel: " + "setting %d (%d) is below the minimum " + "value (%d)\n", i, j, + (int)data->pwm_settings[i][j], + (int)min); + goto abituguru_detect_no_pwms_exit; + } + if (data->pwm_settings[i][j] > abituguru_pwm_max[j]) { + ABIT_UGURU_DEBUG(2, " pwm channel %d does " + "not seem to be a pwm channel: " + "setting %d (%d) is above the maximum " + "value (%d)\n", i, j, + (int)data->pwm_settings[i][j], + (int)abituguru_pwm_max[j]); + goto abituguru_detect_no_pwms_exit; + } + } + + /* check that min temp < max temp and min pwm < max pwm */ + if (data->pwm_settings[i][1] >= data->pwm_settings[i][2]) { + ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem " + "to be a pwm channel: min pwm (%d) >= " + "max pwm (%d)\n", i, + (int)data->pwm_settings[i][1], + (int)data->pwm_settings[i][2]); + break; + } + if (data->pwm_settings[i][3] >= data->pwm_settings[i][4]) { + ABIT_UGURU_DEBUG(2, " pwm channel %d does not seem " + "to be a pwm channel: min temp (%d) >= " + "max temp (%d)\n", i, + (int)data->pwm_settings[i][3], + (int)data->pwm_settings[i][4]); + break; + } + } + +abituguru_detect_no_pwms_exit: + data->pwms = i; + ABIT_UGURU_DEBUG(2, " found: %d PWM outputs\n", (int)data->pwms); +} + +/* Following are the sysfs callback functions. These functions expect: + sensor_device_attribute_2->index: sensor address/offset in the bank + sensor_device_attribute_2->nr: register offset, bitmask or NA. */ +static struct abituguru_data *abituguru_update_device(struct device *dev); + +static ssize_t show_bank1_value(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = abituguru_update_device(dev); + if (!data) + return -EIO; + return sprintf(buf, "%d\n", (data->bank1_value[attr->index] * + data->bank1_max_value[attr->index] + 128) / 255); +} + +static ssize_t show_bank1_setting(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", + (data->bank1_settings[attr->index][attr->nr] * + data->bank1_max_value[attr->index] + 128) / 255); +} + +static ssize_t show_bank2_value(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = abituguru_update_device(dev); + if (!data) + return -EIO; + return sprintf(buf, "%d\n", (data->bank2_value[attr->index] * + ABIT_UGURU_FAN_MAX + 128) / 255); +} + +static ssize_t show_bank2_setting(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", + (data->bank2_settings[attr->index][attr->nr] * + ABIT_UGURU_FAN_MAX + 128) / 255); +} + +static ssize_t store_bank1_setting(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = dev_get_drvdata(dev); + u8 val = (simple_strtoul(buf, NULL, 10) * 255 + + data->bank1_max_value[attr->index]/2) / + data->bank1_max_value[attr->index]; + ssize_t ret = count; + + mutex_lock(&data->update_lock); + if (data->bank1_settings[attr->index][attr->nr] != val) { + u8 orig_val = data->bank1_settings[attr->index][attr->nr]; + data->bank1_settings[attr->index][attr->nr] = val; + if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK1 + 2, + attr->index, data->bank1_settings[attr->index], + 3) <= attr->nr) { + data->bank1_settings[attr->index][attr->nr] = orig_val; + ret = -EIO; + } + } + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t store_bank2_setting(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = dev_get_drvdata(dev); + u8 val = (simple_strtoul(buf, NULL, 10)*255 + ABIT_UGURU_FAN_MAX/2) / + ABIT_UGURU_FAN_MAX; + ssize_t ret = count; + + /* this check can be done before taking the lock */ + if ((val < abituguru_bank2_min_threshold) || + (val > abituguru_bank2_max_threshold)) + return -EINVAL; + + mutex_lock(&data->update_lock); + if (data->bank2_settings[attr->index][attr->nr] != val) { + u8 orig_val = data->bank2_settings[attr->index][attr->nr]; + data->bank2_settings[attr->index][attr->nr] = val; + if (abituguru_write(data, ABIT_UGURU_SENSOR_BANK2 + 2, + attr->index, data->bank2_settings[attr->index], + 2) <= attr->nr) { + data->bank2_settings[attr->index][attr->nr] = orig_val; + ret = -EIO; + } + } + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t show_bank1_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = abituguru_update_device(dev); + if (!data) + return -EIO; + /* See if the alarm bit for this sensor is set, and if the + alarm matches the type of alarm we're looking for (for volt + it can be either low or high). The type is stored in a few + readonly bits in the settings part of the relevant sensor. + The bitmask of the type is passed to us in attr->nr. */ + if ((data->alarms[attr->index / 8] & (0x01 << (attr->index % 8))) && + (data->bank1_settings[attr->index][0] & attr->nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t show_bank2_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = abituguru_update_device(dev); + if (!data) + return -EIO; + if (data->alarms[2] & (0x01 << attr->index)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t show_bank1_mask(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = dev_get_drvdata(dev); + if (data->bank1_settings[attr->index][0] & attr->nr) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t show_bank2_mask(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = dev_get_drvdata(dev); + if (data->bank2_settings[attr->index][0] & attr->nr) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t store_bank1_mask(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = dev_get_drvdata(dev); + int mask = simple_strtoul(buf, NULL, 10); + ssize_t ret = count; + u8 orig_val; + + mutex_lock(&data->update_lock); + orig_val = data->bank1_settings[attr->index][0]; + + if (mask) + data->bank1_settings[attr->index][0] |= attr->nr; + else + data->bank1_settings[attr->index][0] &= ~attr->nr; + + if ((data->bank1_settings[attr->index][0] != orig_val) && + (abituguru_write(data, + ABIT_UGURU_SENSOR_BANK1 + 2, attr->index, + data->bank1_settings[attr->index], 3) < 1)) { + data->bank1_settings[attr->index][0] = orig_val; + ret = -EIO; + } + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t store_bank2_mask(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = dev_get_drvdata(dev); + int mask = simple_strtoul(buf, NULL, 10); + ssize_t ret = count; + u8 orig_val; + + mutex_lock(&data->update_lock); + orig_val = data->bank2_settings[attr->index][0]; + + if (mask) + data->bank2_settings[attr->index][0] |= attr->nr; + else + data->bank2_settings[attr->index][0] &= ~attr->nr; + + if ((data->bank2_settings[attr->index][0] != orig_val) && + (abituguru_write(data, + ABIT_UGURU_SENSOR_BANK2 + 2, attr->index, + data->bank2_settings[attr->index], 2) < 1)) { + data->bank2_settings[attr->index][0] = orig_val; + ret = -EIO; + } + mutex_unlock(&data->update_lock); + return ret; +} + +/* Fan PWM (speed control) */ +static ssize_t show_pwm_setting(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", data->pwm_settings[attr->index][attr->nr] * + abituguru_pwm_settings_multiplier[attr->nr]); +} + +static ssize_t store_pwm_setting(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = dev_get_drvdata(dev); + u8 min, val = (simple_strtoul(buf, NULL, 10) + + abituguru_pwm_settings_multiplier[attr->nr]/2) / + abituguru_pwm_settings_multiplier[attr->nr]; + ssize_t ret = count; + + /* special case pwm1 min pwm% */ + if ((attr->index == 0) && ((attr->nr == 1) || (attr->nr == 2))) + min = 77; + else + min = abituguru_pwm_min[attr->nr]; + + /* this check can be done before taking the lock */ + if ((val < min) || (val > abituguru_pwm_max[attr->nr])) + return -EINVAL; + + mutex_lock(&data->update_lock); + /* this check needs to be done after taking the lock */ + if ((attr->nr & 1) && + (val >= data->pwm_settings[attr->index][attr->nr + 1])) + ret = -EINVAL; + else if (!(attr->nr & 1) && + (val <= data->pwm_settings[attr->index][attr->nr - 1])) + ret = -EINVAL; + else if (data->pwm_settings[attr->index][attr->nr] != val) { + u8 orig_val = data->pwm_settings[attr->index][attr->nr]; + data->pwm_settings[attr->index][attr->nr] = val; + if (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1, + attr->index, data->pwm_settings[attr->index], + 5) <= attr->nr) { + data->pwm_settings[attr->index][attr->nr] = + orig_val; + ret = -EIO; + } + } + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t show_pwm_sensor(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = dev_get_drvdata(dev); + int i; + /* We need to walk to the temp sensor addresses to find what + the userspace id of the configured temp sensor is. */ + for (i = 0; i < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]; i++) + if (data->bank1_address[ABIT_UGURU_TEMP_SENSOR][i] == + (data->pwm_settings[attr->index][0] & 0x0F)) + return sprintf(buf, "%d\n", i+1); + + return -ENXIO; +} + +static ssize_t store_pwm_sensor(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = dev_get_drvdata(dev); + unsigned long val = simple_strtoul(buf, NULL, 10) - 1; + ssize_t ret = count; + + mutex_lock(&data->update_lock); + if (val < data->bank1_sensors[ABIT_UGURU_TEMP_SENSOR]) { + u8 orig_val = data->pwm_settings[attr->index][0]; + u8 address = data->bank1_address[ABIT_UGURU_TEMP_SENSOR][val]; + data->pwm_settings[attr->index][0] &= 0xF0; + data->pwm_settings[attr->index][0] |= address; + if (data->pwm_settings[attr->index][0] != orig_val) { + if (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1, + attr->index, + data->pwm_settings[attr->index], + 5) < 1) { + data->pwm_settings[attr->index][0] = orig_val; + ret = -EIO; + } + } + } + else + ret = -EINVAL; + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = dev_get_drvdata(dev); + int res = 0; + if (data->pwm_settings[attr->index][0] & ABIT_UGURU_FAN_PWM_ENABLE) + res = 2; + return sprintf(buf, "%d\n", res); +} + +static ssize_t store_pwm_enable(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct abituguru_data *data = dev_get_drvdata(dev); + u8 orig_val, user_val = simple_strtoul(buf, NULL, 10); + ssize_t ret = count; + + mutex_lock(&data->update_lock); + orig_val = data->pwm_settings[attr->index][0]; + switch (user_val) { + case 0: + data->pwm_settings[attr->index][0] &= + ~ABIT_UGURU_FAN_PWM_ENABLE; + break; + case 2: + data->pwm_settings[attr->index][0] |= + ABIT_UGURU_FAN_PWM_ENABLE; + break; + default: + ret = -EINVAL; + } + if ((data->pwm_settings[attr->index][0] != orig_val) && + (abituguru_write(data, ABIT_UGURU_FAN_PWM + 1, + attr->index, data->pwm_settings[attr->index], + 5) < 1)) { + data->pwm_settings[attr->index][0] = orig_val; + ret = -EIO; + } + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t show_name(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + return sprintf(buf, "%s\n", ABIT_UGURU_NAME); +} + +/* Sysfs attr templates, the real entries are generated automatically. */ +static const +struct sensor_device_attribute_2 abituguru_sysfs_bank1_templ[2][9] = { + { + SENSOR_ATTR_2(in%d_input, 0444, show_bank1_value, NULL, 0, 0), + SENSOR_ATTR_2(in%d_min, 0644, show_bank1_setting, + store_bank1_setting, 1, 0), + SENSOR_ATTR_2(in%d_min_alarm, 0444, show_bank1_alarm, NULL, + ABIT_UGURU_VOLT_LOW_ALARM_FLAG, 0), + SENSOR_ATTR_2(in%d_max, 0644, show_bank1_setting, + store_bank1_setting, 2, 0), + SENSOR_ATTR_2(in%d_max_alarm, 0444, show_bank1_alarm, NULL, + ABIT_UGURU_VOLT_HIGH_ALARM_FLAG, 0), + SENSOR_ATTR_2(in%d_beep, 0644, show_bank1_mask, + store_bank1_mask, ABIT_UGURU_BEEP_ENABLE, 0), + SENSOR_ATTR_2(in%d_shutdown, 0644, show_bank1_mask, + store_bank1_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0), + SENSOR_ATTR_2(in%d_min_alarm_enable, 0644, show_bank1_mask, + store_bank1_mask, ABIT_UGURU_VOLT_LOW_ALARM_ENABLE, 0), + SENSOR_ATTR_2(in%d_max_alarm_enable, 0644, show_bank1_mask, + store_bank1_mask, ABIT_UGURU_VOLT_HIGH_ALARM_ENABLE, 0), + }, { + SENSOR_ATTR_2(temp%d_input, 0444, show_bank1_value, NULL, 0, 0), + SENSOR_ATTR_2(temp%d_alarm, 0444, show_bank1_alarm, NULL, + ABIT_UGURU_TEMP_HIGH_ALARM_FLAG, 0), + SENSOR_ATTR_2(temp%d_max, 0644, show_bank1_setting, + store_bank1_setting, 1, 0), + SENSOR_ATTR_2(temp%d_crit, 0644, show_bank1_setting, + store_bank1_setting, 2, 0), + SENSOR_ATTR_2(temp%d_beep, 0644, show_bank1_mask, + store_bank1_mask, ABIT_UGURU_BEEP_ENABLE, 0), + SENSOR_ATTR_2(temp%d_shutdown, 0644, show_bank1_mask, + store_bank1_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0), + SENSOR_ATTR_2(temp%d_alarm_enable, 0644, show_bank1_mask, + store_bank1_mask, ABIT_UGURU_TEMP_HIGH_ALARM_ENABLE, 0), + } +}; + +static const struct sensor_device_attribute_2 abituguru_sysfs_fan_templ[6] = { + SENSOR_ATTR_2(fan%d_input, 0444, show_bank2_value, NULL, 0, 0), + SENSOR_ATTR_2(fan%d_alarm, 0444, show_bank2_alarm, NULL, 0, 0), + SENSOR_ATTR_2(fan%d_min, 0644, show_bank2_setting, + store_bank2_setting, 1, 0), + SENSOR_ATTR_2(fan%d_beep, 0644, show_bank2_mask, + store_bank2_mask, ABIT_UGURU_BEEP_ENABLE, 0), + SENSOR_ATTR_2(fan%d_shutdown, 0644, show_bank2_mask, + store_bank2_mask, ABIT_UGURU_SHUTDOWN_ENABLE, 0), + SENSOR_ATTR_2(fan%d_alarm_enable, 0644, show_bank2_mask, + store_bank2_mask, ABIT_UGURU_FAN_LOW_ALARM_ENABLE, 0), +}; + +static const struct sensor_device_attribute_2 abituguru_sysfs_pwm_templ[6] = { + SENSOR_ATTR_2(pwm%d_enable, 0644, show_pwm_enable, + store_pwm_enable, 0, 0), + SENSOR_ATTR_2(pwm%d_auto_channels_temp, 0644, show_pwm_sensor, + store_pwm_sensor, 0, 0), + SENSOR_ATTR_2(pwm%d_auto_point1_pwm, 0644, show_pwm_setting, + store_pwm_setting, 1, 0), + SENSOR_ATTR_2(pwm%d_auto_point2_pwm, 0644, show_pwm_setting, + store_pwm_setting, 2, 0), + SENSOR_ATTR_2(pwm%d_auto_point1_temp, 0644, show_pwm_setting, + store_pwm_setting, 3, 0), + SENSOR_ATTR_2(pwm%d_auto_point2_temp, 0644, show_pwm_setting, + store_pwm_setting, 4, 0), +}; + +static struct sensor_device_attribute_2 abituguru_sysfs_attr[] = { + SENSOR_ATTR_2(name, 0444, show_name, NULL, 0, 0), +}; + +static int __devinit abituguru_probe(struct platform_device *pdev) +{ + struct abituguru_data *data; + int i, j, used, sysfs_names_free, sysfs_attr_i, res = -ENODEV; + char *sysfs_filename; + + /* El weirdo probe order, to keep the sysfs order identical to the + BIOS and window-appliction listing order. */ + const u8 probe_order[ABIT_UGURU_MAX_BANK1_SENSORS] = { + 0x00, 0x01, 0x03, 0x04, 0x0A, 0x08, 0x0E, 0x02, + 0x09, 0x06, 0x05, 0x0B, 0x0F, 0x0D, 0x07, 0x0C }; + + if (!(data = kzalloc(sizeof(struct abituguru_data), GFP_KERNEL))) + return -ENOMEM; + + data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; + mutex_init(&data->update_lock); + platform_set_drvdata(pdev, data); + + /* See if the uGuru is ready */ + if (inb_p(data->addr + ABIT_UGURU_DATA) == ABIT_UGURU_STATUS_INPUT) + data->uguru_ready = 1; + + /* Completely read the uGuru this has 2 purposes: + - testread / see if one really is there. + - make an in memory copy of all the uguru settings for future use. */ + if (abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, + data->alarms, 3, ABIT_UGURU_MAX_RETRIES) != 3) + goto abituguru_probe_error; + + for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) { + if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1, i, + &data->bank1_value[i], 1, + ABIT_UGURU_MAX_RETRIES) != 1) + goto abituguru_probe_error; + if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK1+1, i, + data->bank1_settings[i], 3, + ABIT_UGURU_MAX_RETRIES) != 3) + goto abituguru_probe_error; + } + /* Note: We don't know how many bank2 sensors / pwms there really are, + but in order to "detect" this we need to read the maximum amount + anyways. If we read sensors/pwms not there we'll just read crap + this can't hurt. We need the detection because we don't want + unwanted writes, which will hurt! */ + for (i = 0; i < ABIT_UGURU_MAX_BANK2_SENSORS; i++) { + if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2, i, + &data->bank2_value[i], 1, + ABIT_UGURU_MAX_RETRIES) != 1) + goto abituguru_probe_error; + if (abituguru_read(data, ABIT_UGURU_SENSOR_BANK2+1, i, + data->bank2_settings[i], 2, + ABIT_UGURU_MAX_RETRIES) != 2) + goto abituguru_probe_error; + } + for (i = 0; i < ABIT_UGURU_MAX_PWMS; i++) { + if (abituguru_read(data, ABIT_UGURU_FAN_PWM, i, + data->pwm_settings[i], 5, + ABIT_UGURU_MAX_RETRIES) != 5) + goto abituguru_probe_error; + } + data->last_updated = jiffies; + + /* Detect sensor types and fill the sysfs attr for bank1 */ + sysfs_attr_i = 0; + sysfs_filename = data->sysfs_names; + sysfs_names_free = ABITUGURU_SYSFS_NAMES_LENGTH; + for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) { + res = abituguru_detect_bank1_sensor_type(data, probe_order[i]); + if (res < 0) + goto abituguru_probe_error; + if (res == ABIT_UGURU_NC) + continue; + + /* res 1 (temp) sensors have 7 sysfs entries, 0 (in) 9 */ + for (j = 0; j < (res ? 7 : 9); j++) { + used = snprintf(sysfs_filename, sysfs_names_free, + abituguru_sysfs_bank1_templ[res][j].dev_attr. + attr.name, data->bank1_sensors[res] + res) + + 1; + data->sysfs_attr[sysfs_attr_i] = + abituguru_sysfs_bank1_templ[res][j]; + data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name = + sysfs_filename; + data->sysfs_attr[sysfs_attr_i].index = probe_order[i]; + sysfs_filename += used; + sysfs_names_free -= used; + sysfs_attr_i++; + } + data->bank1_max_value[probe_order[i]] = + abituguru_bank1_max_value[res]; + data->bank1_address[res][data->bank1_sensors[res]] = + probe_order[i]; + data->bank1_sensors[res]++; + } + /* Detect number of sensors and fill the sysfs attr for bank2 (fans) */ + abituguru_detect_no_bank2_sensors(data); + for (i = 0; i < data->bank2_sensors; i++) { + for (j = 0; j < ARRAY_SIZE(abituguru_sysfs_fan_templ); j++) { + used = snprintf(sysfs_filename, sysfs_names_free, + abituguru_sysfs_fan_templ[j].dev_attr.attr.name, + i + 1) + 1; + data->sysfs_attr[sysfs_attr_i] = + abituguru_sysfs_fan_templ[j]; + data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name = + sysfs_filename; + data->sysfs_attr[sysfs_attr_i].index = i; + sysfs_filename += used; + sysfs_names_free -= used; + sysfs_attr_i++; + } + } + /* Detect number of sensors and fill the sysfs attr for pwms */ + abituguru_detect_no_pwms(data); + for (i = 0; i < data->pwms; i++) { + for (j = 0; j < ARRAY_SIZE(abituguru_sysfs_pwm_templ); j++) { + used = snprintf(sysfs_filename, sysfs_names_free, + abituguru_sysfs_pwm_templ[j].dev_attr.attr.name, + i + 1) + 1; + data->sysfs_attr[sysfs_attr_i] = + abituguru_sysfs_pwm_templ[j]; + data->sysfs_attr[sysfs_attr_i].dev_attr.attr.name = + sysfs_filename; + data->sysfs_attr[sysfs_attr_i].index = i; + sysfs_filename += used; + sysfs_names_free -= used; + sysfs_attr_i++; + } + } + /* Fail safe check, this should never happen! */ + if (sysfs_names_free < 0) { + printk(KERN_ERR ABIT_UGURU_NAME ": Fatal error ran out of " + "space for sysfs attr names. This should never " + "happen please report to the abituguru maintainer " + "(see MAINTAINERS)\n"); + res = -ENAMETOOLONG; + goto abituguru_probe_error; + } + printk(KERN_INFO ABIT_UGURU_NAME ": found Abit uGuru\n"); + + /* Register sysfs hooks */ + data->class_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->class_dev)) { + res = PTR_ERR(data->class_dev); + goto abituguru_probe_error; + } + for (i = 0; i < sysfs_attr_i; i++) + device_create_file(&pdev->dev, &data->sysfs_attr[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) + device_create_file(&pdev->dev, + &abituguru_sysfs_attr[i].dev_attr); + + return 0; + +abituguru_probe_error: + kfree(data); + return res; +} + +static int __devexit abituguru_remove(struct platform_device *pdev) +{ + struct abituguru_data *data = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + hwmon_device_unregister(data->class_dev); + kfree(data); + + return 0; +} + +static struct abituguru_data *abituguru_update_device(struct device *dev) +{ + int i, err; + struct abituguru_data *data = dev_get_drvdata(dev); + /* fake a complete successful read if no update necessary. */ + char success = 1; + + mutex_lock(&data->update_lock); + if (time_after(jiffies, data->last_updated + HZ)) { + success = 0; + if ((err = abituguru_read(data, ABIT_UGURU_ALARM_BANK, 0, + data->alarms, 3, 0)) != 3) + goto LEAVE_UPDATE; + for (i = 0; i < ABIT_UGURU_MAX_BANK1_SENSORS; i++) { + if ((err = abituguru_read(data, + ABIT_UGURU_SENSOR_BANK1, i, + &data->bank1_value[i], 1, 0)) != 1) + goto LEAVE_UPDATE; + if ((err = abituguru_read(data, + ABIT_UGURU_SENSOR_BANK1 + 1, i, + data->bank1_settings[i], 3, 0)) != 3) + goto LEAVE_UPDATE; + } + for (i = 0; i < data->bank2_sensors; i++) + if ((err = abituguru_read(data, + ABIT_UGURU_SENSOR_BANK2, i, + &data->bank2_value[i], 1, 0)) != 1) + goto LEAVE_UPDATE; + /* success! */ + success = 1; + data->update_timeouts = 0; +LEAVE_UPDATE: + /* handle timeout condition */ + if (err == -EBUSY) { + /* No overflow please */ + if (data->update_timeouts < 255u) + data->update_timeouts++; + if (data->update_timeouts <= ABIT_UGURU_MAX_TIMEOUTS) { + ABIT_UGURU_DEBUG(3, "timeout exceeded, will " + "try again next update\n"); + /* Just a timeout, fake a successful read */ + success = 1; + } else + ABIT_UGURU_DEBUG(1, "timeout exceeded %d " + "times waiting for more input state\n", + (int)data->update_timeouts); + } + /* On success set last_updated */ + if (success) + data->last_updated = jiffies; + } + mutex_unlock(&data->update_lock); + + if (success) + return data; + else + return NULL; +} + +static struct platform_driver abituguru_driver = { + .driver = { + .owner = THIS_MODULE, + .name = ABIT_UGURU_NAME, + }, + .probe = abituguru_probe, + .remove = __devexit_p(abituguru_remove), +}; + +static int __init abituguru_detect(void) +{ + /* See if there is an uguru there. After a reboot uGuru will hold 0x00 + at DATA and 0xAC, when this driver has already been loaded once + DATA will hold 0x08. For most uGuru's CMD will hold 0xAC in either + scenario but some will hold 0x00. + Some uGuru's initally hold 0x09 at DATA and will only hold 0x08 + after reading CMD first, so CMD must be read first! */ + u8 cmd_val = inb_p(ABIT_UGURU_BASE + ABIT_UGURU_CMD); + u8 data_val = inb_p(ABIT_UGURU_BASE + ABIT_UGURU_DATA); + if (((data_val == 0x00) || (data_val == 0x08)) && + ((cmd_val == 0x00) || (cmd_val == 0xAC))) + return ABIT_UGURU_BASE; + + ABIT_UGURU_DEBUG(2, "no Abit uGuru found, data = 0x%02X, cmd = " + "0x%02X\n", (unsigned int)data_val, (unsigned int)cmd_val); + + if (force) { + printk(KERN_INFO ABIT_UGURU_NAME ": Assuming Abit uGuru is " + "present because of \"force\" parameter\n"); + return ABIT_UGURU_BASE; + } + + /* No uGuru found */ + return -ENODEV; +} + +static struct platform_device *abituguru_pdev; + +static int __init abituguru_init(void) +{ + int address, err; + struct resource res = { .flags = IORESOURCE_IO }; + + address = abituguru_detect(); + if (address < 0) + return address; + + err = platform_driver_register(&abituguru_driver); + if (err) + goto exit; + + abituguru_pdev = platform_device_alloc(ABIT_UGURU_NAME, address); + if (!abituguru_pdev) { + printk(KERN_ERR ABIT_UGURU_NAME + ": Device allocation failed\n"); + err = -ENOMEM; + goto exit_driver_unregister; + } + + res.start = address; + res.end = address + ABIT_UGURU_REGION_LENGTH - 1; + res.name = ABIT_UGURU_NAME; + + err = platform_device_add_resources(abituguru_pdev, &res, 1); + if (err) { + printk(KERN_ERR ABIT_UGURU_NAME + ": Device resource addition failed (%d)\n", err); + goto exit_device_put; + } + + err = platform_device_add(abituguru_pdev); + if (err) { + printk(KERN_ERR ABIT_UGURU_NAME + ": Device addition failed (%d)\n", err); + goto exit_device_put; + } + + return 0; + +exit_device_put: + platform_device_put(abituguru_pdev); +exit_driver_unregister: + platform_driver_unregister(&abituguru_driver); +exit: + return err; +} + +static void __exit abituguru_exit(void) +{ + platform_device_unregister(abituguru_pdev); + platform_driver_unregister(&abituguru_driver); +} + +MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>"); +MODULE_DESCRIPTION("Abit uGuru Sensor device"); +MODULE_LICENSE("GPL"); + +module_init(abituguru_init); +module_exit(abituguru_exit); diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index 885465df6e6..fd72440faf7 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -99,10 +99,6 @@ superio_exit(int base) #define ADDR_REG_OFFSET 0 #define DATA_REG_OFFSET 1 -static struct resource f71805f_resource __initdata = { - .flags = IORESOURCE_IO, -}; - /* * Registers */ @@ -782,6 +778,11 @@ static struct platform_driver f71805f_driver = { static int __init f71805f_device_add(unsigned short address) { + struct resource res = { + .start = address, + .end = address + REGION_LENGTH - 1, + .flags = IORESOURCE_IO, + }; int err; pdev = platform_device_alloc(DRVNAME, address); @@ -791,10 +792,8 @@ static int __init f71805f_device_add(unsigned short address) goto exit; } - f71805f_resource.start = address; - f71805f_resource.end = address + REGION_LENGTH - 1; - f71805f_resource.name = pdev->name; - err = platform_device_add_resources(pdev, &f71805f_resource, 1); + res.name = pdev->name; + err = platform_device_add_resources(pdev, &res, 1); if (err) { printk(KERN_ERR DRVNAME ": Device resource addition failed " "(%d)\n", err); diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c index 1659f6c4145..42b632889dd 100644 --- a/drivers/hwmon/hdaps.c +++ b/drivers/hwmon/hdaps.c @@ -41,7 +41,7 @@ #define HDAPS_PORT_STATE 0x1611 /* device state */ #define HDAPS_PORT_YPOS 0x1612 /* y-axis position */ #define HDAPS_PORT_XPOS 0x1614 /* x-axis position */ -#define HDAPS_PORT_TEMP1 0x1616 /* device temperature, in celcius */ +#define HDAPS_PORT_TEMP1 0x1616 /* device temperature, in Celsius */ #define HDAPS_PORT_YVAR 0x1617 /* y-axis variance (what is this?) */ #define HDAPS_PORT_XVAR 0x1619 /* x-axis variance (what is this?) */ #define HDAPS_PORT_TEMP2 0x161b /* device temperature (again?) */ @@ -522,13 +522,15 @@ static int __init hdaps_init(void) { int ret; - /* Note that DMI_MATCH(...,"ThinkPad T42") will match "ThinkPad T42p" */ + /* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match + "ThinkPad T42p", so the order of the entries matters */ struct dmi_system_id hdaps_whitelist[] = { HDAPS_DMI_MATCH_NORMAL("ThinkPad H"), HDAPS_DMI_MATCH_INVERT("ThinkPad R50p"), HDAPS_DMI_MATCH_NORMAL("ThinkPad R50"), HDAPS_DMI_MATCH_NORMAL("ThinkPad R51"), HDAPS_DMI_MATCH_NORMAL("ThinkPad R52"), + HDAPS_DMI_MATCH_NORMAL("ThinkPad H"), /* R52 (1846AQG) */ HDAPS_DMI_MATCH_INVERT("ThinkPad T41p"), HDAPS_DMI_MATCH_NORMAL("ThinkPad T41"), HDAPS_DMI_MATCH_INVERT("ThinkPad T42p"), @@ -536,9 +538,9 @@ static int __init hdaps_init(void) HDAPS_DMI_MATCH_NORMAL("ThinkPad T43"), HDAPS_DMI_MATCH_LENOVO("ThinkPad T60p"), HDAPS_DMI_MATCH_NORMAL("ThinkPad X40"), - HDAPS_DMI_MATCH_NORMAL("ThinkPad X41 Tablet"), HDAPS_DMI_MATCH_NORMAL("ThinkPad X41"), HDAPS_DMI_MATCH_LENOVO("ThinkPad X60"), + HDAPS_DMI_MATCH_NORMAL("ThinkPad Z60m"), { .ident = NULL } }; diff --git a/drivers/hwmon/hwmon-vid.c b/drivers/hwmon/hwmon-vid.c index a74a44f16f5..a6764ff0080 100644 --- a/drivers/hwmon/hwmon-vid.c +++ b/drivers/hwmon/hwmon-vid.c @@ -58,11 +58,20 @@ doesn't seem to be any named specification for these. The conversion tables are detailed directly in the various Pentium M datasheets: http://www.intel.com/design/intarch/pentiumm/docs_pentiumm.htm + + The 14 specification corresponds to Intel Core series. There + doesn't seem to be any named specification for these. The conversion + tables are detailed directly in the various Pentium Core datasheets: + http://www.intel.com/design/mobile/datashts/309221.htm + + The 110 (VRM 11) specification corresponds to Intel Conroe based series. + http://www.intel.com/design/processor/applnots/313214.htm */ /* vrm is the VRM/VRD document version multiplied by 10. - val is the 4-, 5- or 6-bit VID code. - Returned value is in mV to avoid floating point in the kernel. */ + val is the 4-bit or more VID code. + Returned value is in mV to avoid floating point in the kernel. + Some VID have some bits in uV scale, this is rounded to mV */ int vid_from_reg(int val, u8 vrm) { int vid; @@ -70,26 +79,36 @@ int vid_from_reg(int val, u8 vrm) switch(vrm) { case 100: /* VRD 10.0 */ + /* compute in uV, round to mV */ + val &= 0x3f; if((val & 0x1f) == 0x1f) return 0; if((val & 0x1f) <= 0x09 || val == 0x0a) - vid = 10875 - (val & 0x1f) * 250; + vid = 1087500 - (val & 0x1f) * 25000; else - vid = 18625 - (val & 0x1f) * 250; + vid = 1862500 - (val & 0x1f) * 25000; if(val & 0x20) - vid -= 125; - vid /= 10; /* only return 3 dec. places for now */ - return vid; + vid -= 12500; + return((vid + 500) / 1000); + case 110: /* Intel Conroe */ + /* compute in uV, round to mV */ + val &= 0xff; + if(((val & 0x7e) == 0xfe) || (!(val & 0x7e))) + return 0; + return((1600000 - (val - 2) * 6250 + 500) / 1000); case 24: /* Opteron processor */ + val &= 0x1f; return(val == 0x1f ? 0 : 1550 - val * 25); case 91: /* VRM 9.1 */ case 90: /* VRM 9.0 */ + val &= 0x1f; return(val == 0x1f ? 0 : 1850 - val * 25); case 85: /* VRM 8.5 */ + val &= 0x1f; return((val & 0x10 ? 25 : 0) + ((val & 0x0f) > 0x04 ? 2050 : 1250) - ((val & 0x0f) * 50)); @@ -98,14 +117,21 @@ int vid_from_reg(int val, u8 vrm) val &= 0x0f; /* fall through */ case 82: /* VRM 8.2 */ + val &= 0x1f; return(val == 0x1f ? 0 : val & 0x10 ? 5100 - (val) * 100 : 2050 - (val) * 50); case 17: /* Intel IMVP-II */ + val &= 0x1f; return(val & 0x10 ? 975 - (val & 0xF) * 25 : 1750 - val * 50); case 13: - return(1708 - (val & 0x3f) * 16); + val &= 0x3f; + return(1708 - val * 16); + case 14: /* Intel Core */ + /* compute in uV, round to mV */ + val &= 0x7f; + return(val > 0x77 ? 0 : (1500000 - (val * 12500) + 500) / 1000); default: /* report 0 for unknown */ printk(KERN_INFO "hwmon-vid: requested unknown VRM version\n"); return 0; @@ -138,6 +164,8 @@ static struct vrm_model vrm_models[] = { {X86_VENDOR_INTEL, 0x6, 0x9, ANY, 13}, /* Pentium M (130 nm) */ {X86_VENDOR_INTEL, 0x6, 0xB, ANY, 85}, /* Tualatin */ {X86_VENDOR_INTEL, 0x6, 0xD, ANY, 13}, /* Pentium M (90 nm) */ + {X86_VENDOR_INTEL, 0x6, 0xE, ANY, 14}, /* Intel Core (65 nm) */ + {X86_VENDOR_INTEL, 0x6, 0xF, ANY, 110}, /* Intel Conroe */ {X86_VENDOR_INTEL, 0x6, ANY, ANY, 82}, /* any P6 */ {X86_VENDOR_INTEL, 0x7, ANY, ANY, 0}, /* Itanium */ {X86_VENDOR_INTEL, 0xF, 0x0, ANY, 90}, /* P4 */ diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c new file mode 100644 index 00000000000..6ba84731b9c --- /dev/null +++ b/drivers/hwmon/lm70.c @@ -0,0 +1,165 @@ +/* + * lm70.c + * + * The LM70 is a temperature sensor chip from National Semiconductor (NS). + * Copyright (C) 2006 Kaiwan N Billimoria <kaiwan@designergraphix.com> + * + * The LM70 communicates with a host processor via an SPI/Microwire Bus + * interface. The complete datasheet is available at National's website + * here: + * http://www.national.com/pf/LM/LM70.html + * + * 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. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/sysfs.h> +#include <linux/hwmon.h> +#include <linux/spi/spi.h> +#include <asm/semaphore.h> + +#define DRVNAME "lm70" + +struct lm70 { + struct class_device *cdev; + struct semaphore sem; +}; + +/* sysfs hook function */ +static ssize_t lm70_sense_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + int status, val; + u8 rxbuf[2]; + s16 raw=0; + struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); + + if (down_interruptible(&p_lm70->sem)) + return -ERESTARTSYS; + + /* + * spi_read() requires a DMA-safe buffer; so we use + * spi_write_then_read(), transmitting 0 bytes. + */ + status = spi_write_then_read(spi, NULL, 0, &rxbuf[0], 2); + if (status < 0) { + printk(KERN_WARNING + "spi_write_then_read failed with status %d\n", status); + goto out; + } + dev_dbg(dev, "rxbuf[1] : 0x%x rxbuf[0] : 0x%x\n", rxbuf[1], rxbuf[0]); + + raw = (rxbuf[1] << 8) + rxbuf[0]; + dev_dbg(dev, "raw=0x%x\n", raw); + + /* + * The "raw" temperature read into rxbuf[] is a 16-bit signed 2's + * complement value. Only the MSB 11 bits (1 sign + 10 temperature + * bits) are meaningful; the LSB 5 bits are to be discarded. + * See the datasheet. + * + * Further, each bit represents 0.25 degrees Celsius; so, multiply + * by 0.25. Also multiply by 1000 to represent in millidegrees + * Celsius. + * So it's equivalent to multiplying by 0.25 * 1000 = 250. + */ + val = ((int)raw/32) * 250; + status = sprintf(buf, "%+d\n", val); /* millidegrees Celsius */ +out: + up(&p_lm70->sem); + return status; +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, lm70_sense_temp, NULL); + +/*----------------------------------------------------------------------*/ + +static int __devinit lm70_probe(struct spi_device *spi) +{ + struct lm70 *p_lm70; + int status; + + p_lm70 = kzalloc(sizeof *p_lm70, GFP_KERNEL); + if (!p_lm70) + return -ENOMEM; + + init_MUTEX(&p_lm70->sem); + + /* sysfs hook */ + p_lm70->cdev = hwmon_device_register(&spi->dev); + if (IS_ERR(p_lm70->cdev)) { + dev_dbg(&spi->dev, "hwmon_device_register failed.\n"); + status = PTR_ERR(p_lm70->cdev); + goto out_dev_reg_failed; + } + dev_set_drvdata(&spi->dev, p_lm70); + + if ((status = device_create_file(&spi->dev, &dev_attr_temp1_input))) { + dev_dbg(&spi->dev, "device_create_file failure.\n"); + goto out_dev_create_file_failed; + } + + return 0; + +out_dev_create_file_failed: + hwmon_device_unregister(p_lm70->cdev); +out_dev_reg_failed: + dev_set_drvdata(&spi->dev, NULL); + kfree(p_lm70); + return status; +} + +static int __exit lm70_remove(struct spi_device *spi) +{ + struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); + + device_remove_file(&spi->dev, &dev_attr_temp1_input); + hwmon_device_unregister(p_lm70->cdev); + dev_set_drvdata(&spi->dev, NULL); + kfree(p_lm70); + + return 0; +} + +static struct spi_driver lm70_driver = { + .driver = { + .name = "lm70", + .owner = THIS_MODULE, + }, + .probe = lm70_probe, + .remove = __devexit_p(lm70_remove), +}; + +static int __init init_lm70(void) +{ + return spi_register_driver(&lm70_driver); +} + +static void __exit cleanup_lm70(void) +{ + spi_unregister_driver(&lm70_driver); +} + +module_init(init_lm70); +module_exit(cleanup_lm70); + +MODULE_AUTHOR("Kaiwan N Billimoria"); +MODULE_DESCRIPTION("National Semiconductor LM70 Linux driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c index aac4ec2bf69..2137d7879df 100644 --- a/drivers/hwmon/lm83.c +++ b/drivers/hwmon/lm83.c @@ -12,6 +12,10 @@ * Since the datasheet omits to give the chip stepping code, I give it * here: 0x03 (at register 0xff). * + * Also supports the LM82 temp sensor, which is basically a stripped down + * model of the LM83. Datasheet is here: + * http://www.national.com/pf/LM/LM82.html + * * 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 @@ -52,7 +56,7 @@ static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, * Insmod parameters */ -I2C_CLIENT_INSMOD_1(lm83); +I2C_CLIENT_INSMOD_2(lm83, lm82); /* * The LM83 registers @@ -283,6 +287,9 @@ static int lm83_detect(struct i2c_adapter *adapter, int address, int kind) if (man_id == 0x01) { /* National Semiconductor */ if (chip_id == 0x03) { kind = lm83; + } else + if (chip_id == 0x01) { + kind = lm82; } } @@ -296,6 +303,9 @@ static int lm83_detect(struct i2c_adapter *adapter, int address, int kind) if (kind == lm83) { name = "lm83"; + } else + if (kind == lm82) { + name = "lm82"; } /* We can fill in the remaining client fields */ @@ -319,32 +329,46 @@ static int lm83_detect(struct i2c_adapter *adapter, int address, int kind) goto exit_detach; } + /* + * The LM82 can only monitor one external diode which is + * at the same register as the LM83 temp3 entry - so we + * declare 1 and 3 common, and then 2 and 4 only for the LM83. + */ + device_create_file(&new_client->dev, &sensor_dev_attr_temp1_input.dev_attr); device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp4_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp1_max.dev_attr); device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp4_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp1_crit.dev_attr); device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_crit.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_crit.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp4_crit.dev_attr); + device_create_file(&new_client->dev, &dev_attr_alarms); + if (kind == lm83) { + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_input.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp4_input.dev_attr); + + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_max.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp4_max.dev_attr); + + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_crit.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp4_crit.dev_attr); + } + return 0; exit_detach: diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c new file mode 100644 index 00000000000..bdc4570acf9 --- /dev/null +++ b/drivers/hwmon/smsc47m192.c @@ -0,0 +1,648 @@ +/* + smsc47m192.c - Support for hardware monitoring block of + SMSC LPC47M192 and LPC47M997 Super I/O chips + + Copyright (C) 2006 Hartmut Rick <linux@rick.claranet.de> + + Derived from lm78.c and other chip drivers. + + 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. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/hwmon-vid.h> +#include <linux/err.h> + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(smsc47m192); + +/* SMSC47M192 registers */ +#define SMSC47M192_REG_IN(nr) ((nr)<6 ? (0x20 + (nr)) : \ + (0x50 + (nr) - 6)) +#define SMSC47M192_REG_IN_MAX(nr) ((nr)<6 ? (0x2b + (nr) * 2) : \ + (0x54 + (((nr) - 6) * 2))) +#define SMSC47M192_REG_IN_MIN(nr) ((nr)<6 ? (0x2c + (nr) * 2) : \ + (0x55 + (((nr) - 6) * 2))) +static u8 SMSC47M192_REG_TEMP[3] = { 0x27, 0x26, 0x52 }; +static u8 SMSC47M192_REG_TEMP_MAX[3] = { 0x39, 0x37, 0x58 }; +static u8 SMSC47M192_REG_TEMP_MIN[3] = { 0x3A, 0x38, 0x59 }; +#define SMSC47M192_REG_TEMP_OFFSET(nr) ((nr)==2 ? 0x1e : 0x1f) +#define SMSC47M192_REG_ALARM1 0x41 +#define SMSC47M192_REG_ALARM2 0x42 +#define SMSC47M192_REG_VID 0x47 +#define SMSC47M192_REG_VID4 0x49 +#define SMSC47M192_REG_CONFIG 0x40 +#define SMSC47M192_REG_SFR 0x4f +#define SMSC47M192_REG_COMPANY_ID 0x3e +#define SMSC47M192_REG_VERSION 0x3f + +/* generalised scaling with integer rounding */ +static inline int SCALE(long val, int mul, int div) +{ + if (val < 0) + return (val * mul - div / 2) / div; + else + return (val * mul + div / 2) / div; +} + +/* Conversions */ + +/* smsc47m192 internally scales voltage measurements */ +static const u16 nom_mv[] = { 2500, 2250, 3300, 5000, 12000, 3300, 1500, 1800 }; + +static inline unsigned int IN_FROM_REG(u8 reg, int n) +{ + return SCALE(reg, nom_mv[n], 192); +} + +static inline u8 IN_TO_REG(unsigned long val, int n) +{ + return SENSORS_LIMIT(SCALE(val, 192, nom_mv[n]), 0, 255); +} + +/* TEMP: 0.001 degC units (-128C to +127C) + REG: 1C/bit, two's complement */ +static inline s8 TEMP_TO_REG(int val) +{ + return SENSORS_LIMIT(SCALE(val, 1, 1000), -128000, 127000); +} + +static inline int TEMP_FROM_REG(s8 val) +{ + return val * 1000; +} + +struct smsc47m192_data { + struct i2c_client client; + struct class_device *class_dev; + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[8]; /* Register value */ + u8 in_max[8]; /* Register value */ + u8 in_min[8]; /* Register value */ + s8 temp[3]; /* Register value */ + s8 temp_max[3]; /* Register value */ + s8 temp_min[3]; /* Register value */ + s8 temp_offset[3]; /* Register value */ + u16 alarms; /* Register encoding, combined */ + u8 vid; /* Register encoding, combined */ + u8 vrm; +}; + +static int smsc47m192_attach_adapter(struct i2c_adapter *adapter); +static int smsc47m192_detect(struct i2c_adapter *adapter, int address, + int kind); +static int smsc47m192_detach_client(struct i2c_client *client); +static struct smsc47m192_data *smsc47m192_update_device(struct device *dev); + +static struct i2c_driver smsc47m192_driver = { + .driver = { + .name = "smsc47m192", + }, + .attach_adapter = smsc47m192_attach_adapter, + .detach_client = smsc47m192_detach_client, +}; + +/* Voltages */ +static ssize_t show_in(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct smsc47m192_data *data = smsc47m192_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr], nr)); +} + +static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct smsc47m192_data *data = smsc47m192_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr], nr)); +} + +static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct smsc47m192_data *data = smsc47m192_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr], nr)); +} + +static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m192_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->in_min[nr] = IN_TO_REG(val, nr); + i2c_smbus_write_byte_data(client, SMSC47M192_REG_IN_MIN(nr), + data->in_min[nr]); + up(&data->update_lock); + return count; +} + +static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m192_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->in_max[nr] = IN_TO_REG(val, nr); + i2c_smbus_write_byte_data(client, SMSC47M192_REG_IN_MAX(nr), + data->in_max[nr]); + up(&data->update_lock); + return count; +} + +#define show_in_offset(offset) \ +static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \ + show_in, NULL, offset); \ +static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ + show_in_min, set_in_min, offset); \ +static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ + show_in_max, set_in_max, offset); + +show_in_offset(0) +show_in_offset(1) +show_in_offset(2) +show_in_offset(3) +show_in_offset(4) +show_in_offset(5) +show_in_offset(6) +show_in_offset(7) + +/* Temperatures */ +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct smsc47m192_data *data = smsc47m192_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr])); +} + +static ssize_t show_temp_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct smsc47m192_data *data = smsc47m192_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr])); +} + +static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct smsc47m192_data *data = smsc47m192_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr])); +} + +static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m192_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_min[nr] = TEMP_TO_REG(val); + i2c_smbus_write_byte_data(client, SMSC47M192_REG_TEMP_MIN[nr], + data->temp_min[nr]); + up(&data->update_lock); + return count; +} + +static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m192_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_max[nr] = TEMP_TO_REG(val); + i2c_smbus_write_byte_data(client, SMSC47M192_REG_TEMP_MAX[nr], + data->temp_max[nr]); + up(&data->update_lock); + return count; +} + +static ssize_t show_temp_offset(struct device *dev, struct device_attribute + *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct smsc47m192_data *data = smsc47m192_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_offset[nr])); +} + +static ssize_t set_temp_offset(struct device *dev, struct device_attribute + *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m192_data *data = i2c_get_clientdata(client); + u8 sfr = i2c_smbus_read_byte_data(client, SMSC47M192_REG_SFR); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_offset[nr] = TEMP_TO_REG(val); + if (nr>1) + i2c_smbus_write_byte_data(client, + SMSC47M192_REG_TEMP_OFFSET(nr), data->temp_offset[nr]); + else if (data->temp_offset[nr] != 0) { + /* offset[0] and offset[1] share the same register, + SFR bit 4 activates offset[0] */ + i2c_smbus_write_byte_data(client, SMSC47M192_REG_SFR, + (sfr & 0xef) | (nr==0 ? 0x10 : 0)); + data->temp_offset[1-nr] = 0; + i2c_smbus_write_byte_data(client, + SMSC47M192_REG_TEMP_OFFSET(nr), data->temp_offset[nr]); + } else if ((sfr & 0x10) == (nr==0 ? 0x10 : 0)) + i2c_smbus_write_byte_data(client, + SMSC47M192_REG_TEMP_OFFSET(nr), 0); + up(&data->update_lock); + return count; +} + +#define show_temp_index(index) \ +static SENSOR_DEVICE_ATTR(temp##index##_input, S_IRUGO, \ + show_temp, NULL, index-1); \ +static SENSOR_DEVICE_ATTR(temp##index##_min, S_IRUGO | S_IWUSR, \ + show_temp_min, set_temp_min, index-1); \ +static SENSOR_DEVICE_ATTR(temp##index##_max, S_IRUGO | S_IWUSR, \ + show_temp_max, set_temp_max, index-1); \ +static SENSOR_DEVICE_ATTR(temp##index##_offset, S_IRUGO | S_IWUSR, \ + show_temp_offset, set_temp_offset, index-1); + +show_temp_index(1) +show_temp_index(2) +show_temp_index(3) + +/* VID */ +static ssize_t show_vid(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct smsc47m192_data *data = smsc47m192_update_device(dev); + return sprintf(buf, "%d\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 smsc47m192_data *data = smsc47m192_update_device(dev); + return sprintf(buf, "%d\n", data->vrm); +} + +static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m192_data *data = i2c_get_clientdata(client); + data->vrm = simple_strtoul(buf, NULL, 10); + return count; +} +static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); + +/* Alarms */ +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct smsc47m192_data *data = smsc47m192_update_device(dev); + return sprintf(buf, "%u\n", (data->alarms & nr) ? 1 : 0); +} + +static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 0x0010); +static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 0x0020); +static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 0x0040); +static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 0x4000); +static SENSOR_DEVICE_ATTR(temp3_input_fault, S_IRUGO, show_alarm, NULL, 0x8000); +static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0x0001); +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 0x0002); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 0x0004); +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 0x0008); +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 0x0100); +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 0x0200); +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 0x0400); +static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 0x0800); + +/* This function is called when: + * smsc47m192_driver is inserted (when this module is loaded), for each + available adapter + * when a new adapter is inserted (and smsc47m192_driver is still present) */ +static int smsc47m192_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_probe(adapter, &addr_data, smsc47m192_detect); +} + +static void smsc47m192_init_client(struct i2c_client *client) +{ + int i; + u8 config = i2c_smbus_read_byte_data(client, SMSC47M192_REG_CONFIG); + u8 sfr = i2c_smbus_read_byte_data(client, SMSC47M192_REG_SFR); + + /* select cycle mode (pause 1 sec between updates) */ + i2c_smbus_write_byte_data(client, SMSC47M192_REG_SFR, + (sfr & 0xfd) | 0x02); + if (!(config & 0x01)) { + /* initialize alarm limits */ + for (i=0; i<8; i++) { + i2c_smbus_write_byte_data(client, + SMSC47M192_REG_IN_MIN(i), 0); + i2c_smbus_write_byte_data(client, + SMSC47M192_REG_IN_MAX(i), 0xff); + } + for (i=0; i<3; i++) { + i2c_smbus_write_byte_data(client, + SMSC47M192_REG_TEMP_MIN[i], 0x80); + i2c_smbus_write_byte_data(client, + SMSC47M192_REG_TEMP_MAX[i], 0x7f); + } + + /* start monitoring */ + i2c_smbus_write_byte_data(client, SMSC47M192_REG_CONFIG, + (config & 0xf7) | 0x01); + } +} + +/* This function is called by i2c_probe */ +static int smsc47m192_detect(struct i2c_adapter *adapter, int address, + int kind) +{ + struct i2c_client *client; + struct smsc47m192_data *data; + int err = 0; + int version, config; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + if (!(data = kzalloc(sizeof(struct smsc47m192_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + client = &data->client; + i2c_set_clientdata(client, data); + client->addr = address; + client->adapter = adapter; + client->driver = &smsc47m192_driver; + + if (kind == 0) + kind = smsc47m192; + + /* Detection criteria from sensors_detect script */ + if (kind < 0) { + if (i2c_smbus_read_byte_data(client, + SMSC47M192_REG_COMPANY_ID) == 0x55 + && ((version = i2c_smbus_read_byte_data(client, + SMSC47M192_REG_VERSION)) & 0xf0) == 0x20 + && (i2c_smbus_read_byte_data(client, + SMSC47M192_REG_VID) & 0x70) == 0x00 + && (i2c_smbus_read_byte_data(client, + SMSC47M192_REG_VID4) & 0xfe) == 0x80) { + dev_info(&adapter->dev, + "found SMSC47M192 or SMSC47M997, " + "version 2, stepping A%d\n", version & 0x0f); + } else { + dev_dbg(&adapter->dev, + "SMSC47M192 detection failed at 0x%02x\n", + address); + goto exit_free; + } + } + + /* Fill in the remaining client fields and put into the global list */ + strlcpy(client->name, "smsc47m192", I2C_NAME_SIZE); + data->vrm = vid_which_vrm(); + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(client))) + goto exit_free; + + /* Initialize the SMSC47M192 chip */ + smsc47m192_init_client(client); + + /* Register sysfs hooks */ + data->class_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_detach; + } + + device_create_file(&client->dev, &sensor_dev_attr_in0_input.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in0_min.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in0_max.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in0_alarm.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in1_input.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in1_min.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in1_max.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in1_alarm.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in2_input.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in2_min.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in2_max.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in2_alarm.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in3_input.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in3_min.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in3_max.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in3_alarm.dev_attr); + + /* Pin 110 is either in4 (+12V) or VID4 */ + config = i2c_smbus_read_byte_data(client, SMSC47M192_REG_CONFIG); + if (!(config & 0x20)) { + device_create_file(&client->dev, + &sensor_dev_attr_in4_input.dev_attr); + device_create_file(&client->dev, + &sensor_dev_attr_in4_min.dev_attr); + device_create_file(&client->dev, + &sensor_dev_attr_in4_max.dev_attr); + device_create_file(&client->dev, + &sensor_dev_attr_in4_alarm.dev_attr); + } + device_create_file(&client->dev, &sensor_dev_attr_in5_input.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in5_min.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in5_max.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in5_alarm.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in6_input.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in6_min.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in6_max.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in6_alarm.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in7_input.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in7_min.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in7_max.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_in7_alarm.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_temp1_input.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_temp1_max.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_temp1_min.dev_attr); + device_create_file(&client->dev, + &sensor_dev_attr_temp1_offset.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_temp1_alarm.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_temp2_input.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_temp2_max.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_temp2_min.dev_attr); + device_create_file(&client->dev, + &sensor_dev_attr_temp2_offset.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_temp2_alarm.dev_attr); + device_create_file(&client->dev, + &sensor_dev_attr_temp2_input_fault.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_temp3_input.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_temp3_max.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_temp3_min.dev_attr); + device_create_file(&client->dev, + &sensor_dev_attr_temp3_offset.dev_attr); + device_create_file(&client->dev, &sensor_dev_attr_temp3_alarm.dev_attr); + device_create_file(&client->dev, + &sensor_dev_attr_temp3_input_fault.dev_attr); + device_create_file(&client->dev, &dev_attr_cpu0_vid); + device_create_file(&client->dev, &dev_attr_vrm); + + return 0; + +exit_detach: + i2c_detach_client(client); +exit_free: + kfree(data); +exit: + return err; +} + +static int smsc47m192_detach_client(struct i2c_client *client) +{ + struct smsc47m192_data *data = i2c_get_clientdata(client); + int err; + + hwmon_device_unregister(data->class_dev); + + if ((err = i2c_detach_client(client))) + return err; + + kfree(data); + + return 0; +} + +static struct smsc47m192_data *smsc47m192_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m192_data *data = i2c_get_clientdata(client); + int i, config; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + u8 sfr = i2c_smbus_read_byte_data(client, SMSC47M192_REG_SFR); + + dev_dbg(&client->dev, "Starting smsc47m192 update\n"); + + for (i = 0; i <= 7; i++) { + data->in[i] = i2c_smbus_read_byte_data(client, + SMSC47M192_REG_IN(i)); + data->in_min[i] = i2c_smbus_read_byte_data(client, + SMSC47M192_REG_IN_MIN(i)); + data->in_max[i] = i2c_smbus_read_byte_data(client, + SMSC47M192_REG_IN_MAX(i)); + } + for (i = 0; i < 3; i++) { + data->temp[i] = i2c_smbus_read_byte_data(client, + SMSC47M192_REG_TEMP[i]); + data->temp_max[i] = i2c_smbus_read_byte_data(client, + SMSC47M192_REG_TEMP_MAX[i]); + data->temp_min[i] = i2c_smbus_read_byte_data(client, + SMSC47M192_REG_TEMP_MIN[i]); + } + for (i = 1; i < 3; i++) + data->temp_offset[i] = i2c_smbus_read_byte_data(client, + SMSC47M192_REG_TEMP_OFFSET(i)); + /* first offset is temp_offset[0] if SFR bit 4 is set, + temp_offset[1] otherwise */ + if (sfr & 0x10) { + data->temp_offset[0] = data->temp_offset[1]; + data->temp_offset[1] = 0; + } else + data->temp_offset[0] = 0; + + data->vid = i2c_smbus_read_byte_data(client, SMSC47M192_REG_VID) + & 0x0f; + config = i2c_smbus_read_byte_data(client, + SMSC47M192_REG_CONFIG); + if (config & 0x20) + data->vid |= (i2c_smbus_read_byte_data(client, + SMSC47M192_REG_VID4) & 0x01) << 4; + data->alarms = i2c_smbus_read_byte_data(client, + SMSC47M192_REG_ALARM1) | + (i2c_smbus_read_byte_data(client, + SMSC47M192_REG_ALARM2) << 8); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init smsc47m192_init(void) +{ + return i2c_add_driver(&smsc47m192_driver); +} + +static void __exit smsc47m192_exit(void) +{ + i2c_del_driver(&smsc47m192_driver); +} + +MODULE_AUTHOR("Hartmut Rick <linux@rick.claranet.de>"); +MODULE_DESCRIPTION("SMSC47M192 driver"); +MODULE_LICENSE("GPL"); + +module_init(smsc47m192_init); +module_exit(smsc47m192_exit); diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index b6bd5685fd3..40301bc6ce1 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -30,10 +30,7 @@ Supports the following chips: Chip #vin #fan #pwm #temp chip_id man_id - w83627ehf - 5 - 3 0x88 0x5ca3 - - This is a preliminary version of the driver, only supporting the - fan and temperature inputs. The chip does much more than that. + w83627ehf 10 5 - 3 0x88 0x5ca3 */ #include <linux/module.h> @@ -121,6 +118,14 @@ superio_exit(void) static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 }; static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c }; +/* The W83627EHF registers for nr=7,8,9 are in bank 5 */ +#define W83627EHF_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \ + (0x554 + (((nr) - 7) * 2))) +#define W83627EHF_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \ + (0x555 + (((nr) - 7) * 2))) +#define W83627EHF_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ + (0x550 + (nr) - 7)) + #define W83627EHF_REG_TEMP1 0x27 #define W83627EHF_REG_TEMP1_HYST 0x3a #define W83627EHF_REG_TEMP1_OVER 0x39 @@ -136,6 +141,10 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 }; #define W83627EHF_REG_DIODE 0x59 #define W83627EHF_REG_SMI_OVT 0x4C +#define W83627EHF_REG_ALARM1 0x459 +#define W83627EHF_REG_ALARM2 0x45A +#define W83627EHF_REG_ALARM3 0x45B + /* * Conversions */ @@ -172,6 +181,20 @@ temp1_to_reg(int temp) return (temp + 500) / 1000; } +/* Some of analog inputs have internal scaling (2x), 8mV is ADC LSB */ + +static u8 scale_in[10] = { 8, 8, 16, 16, 8, 8, 8, 16, 16, 8 }; + +static inline long in_from_reg(u8 reg, u8 nr) +{ + return reg * scale_in[nr]; +} + +static inline u8 in_to_reg(u32 val, u8 nr) +{ + return SENSORS_LIMIT(((val + (scale_in[nr] / 2)) / scale_in[nr]), 0, 255); +} + /* * Data structures and manipulation thereof */ @@ -186,6 +209,9 @@ struct w83627ehf_data { unsigned long last_updated; /* In jiffies */ /* Register values */ + u8 in[10]; /* Register value */ + u8 in_max[10]; /* Register value */ + u8 in_min[10]; /* Register value */ u8 fan[5]; u8 fan_min[5]; u8 fan_div[5]; @@ -196,6 +222,7 @@ struct w83627ehf_data { s16 temp[2]; s16 temp_max[2]; s16 temp_max_hyst[2]; + u32 alarms; }; static inline int is_word_sized(u16 reg) @@ -349,6 +376,16 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) data->fan_div[3] |= (i >> 5) & 0x04; } + /* Measured voltages and limits */ + for (i = 0; i < 10; i++) { + data->in[i] = w83627ehf_read_value(client, + W83627EHF_REG_IN(i)); + data->in_min[i] = w83627ehf_read_value(client, + W83627EHF_REG_IN_MIN(i)); + data->in_max[i] = w83627ehf_read_value(client, + W83627EHF_REG_IN_MAX(i)); + } + /* Measured fan speeds and limits */ for (i = 0; i < 5; i++) { if (!(data->has_fan & (1 << i))) @@ -395,6 +432,13 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) W83627EHF_REG_TEMP_HYST[i]); } + data->alarms = w83627ehf_read_value(client, + W83627EHF_REG_ALARM1) | + (w83627ehf_read_value(client, + W83627EHF_REG_ALARM2) << 8) | + (w83627ehf_read_value(client, + W83627EHF_REG_ALARM3) << 16); + data->last_updated = jiffies; data->valid = 1; } @@ -406,6 +450,109 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) /* * Sysfs callback functions */ +#define show_in_reg(reg) \ +static ssize_t \ +show_##reg(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct w83627ehf_data *data = w83627ehf_update_device(dev); \ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + int nr = sensor_attr->index; \ + return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr)); \ +} +show_in_reg(in) +show_in_reg(in_min) +show_in_reg(in_max) + +#define store_in_reg(REG, reg) \ +static ssize_t \ +store_in_##reg (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83627ehf_data *data = i2c_get_clientdata(client); \ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \ + int nr = sensor_attr->index; \ + u32 val = simple_strtoul(buf, NULL, 10); \ + \ + mutex_lock(&data->update_lock); \ + data->in_##reg[nr] = in_to_reg(val, nr); \ + w83627ehf_write_value(client, W83627EHF_REG_IN_##REG(nr), \ + data->in_##reg[nr]); \ + mutex_unlock(&data->update_lock); \ + return count; \ +} + +store_in_reg(MIN, min) +store_in_reg(MAX, max) + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627ehf_data *data = w83627ehf_update_device(dev); + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + return sprintf(buf, "%u\n", (data->alarms >> nr) & 0x01); +} + +static struct sensor_device_attribute sda_in_input[] = { + SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0), + SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1), + SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2), + SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3), + SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4), + SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5), + SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6), + SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7), + SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8), + SENSOR_ATTR(in9_input, S_IRUGO, show_in, NULL, 9), +}; + +static struct sensor_device_attribute sda_in_alarm[] = { + SENSOR_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0), + SENSOR_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1), + SENSOR_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2), + SENSOR_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3), + SENSOR_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8), + SENSOR_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 21), + SENSOR_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 20), + SENSOR_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 16), + SENSOR_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 17), + SENSOR_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 19), +}; + +static struct sensor_device_attribute sda_in_min[] = { + SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0), + SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1), + SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2), + SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 3), + SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 4), + SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 5), + SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 6), + SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 7), + SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 8), + SENSOR_ATTR(in9_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 9), +}; + +static struct sensor_device_attribute sda_in_max[] = { + SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0), + SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1), + SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2), + SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 3), + SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 4), + SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 5), + SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 6), + SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 7), + SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 8), + SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9), +}; + +static void device_create_file_in(struct device *dev, int i) +{ + device_create_file(dev, &sda_in_input[i].dev_attr); + device_create_file(dev, &sda_in_alarm[i].dev_attr); + device_create_file(dev, &sda_in_min[i].dev_attr); + device_create_file(dev, &sda_in_max[i].dev_attr); +} #define show_fan_reg(reg) \ static ssize_t \ @@ -505,6 +652,14 @@ static struct sensor_device_attribute sda_fan_input[] = { SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4), }; +static struct sensor_device_attribute sda_fan_alarm[] = { + SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6), + SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7), + SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11), + SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 10), + SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 23), +}; + static struct sensor_device_attribute sda_fan_min[] = { SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 0), @@ -529,6 +684,7 @@ static struct sensor_device_attribute sda_fan_div[] = { static void device_create_file_fan(struct device *dev, int i) { device_create_file(dev, &sda_fan_input[i].dev_attr); + device_create_file(dev, &sda_fan_alarm[i].dev_attr); device_create_file(dev, &sda_fan_div[i].dev_attr); device_create_file(dev, &sda_fan_min[i].dev_attr); } @@ -616,6 +772,9 @@ static struct sensor_device_attribute sda_temp[] = { store_temp_max_hyst, 0), SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, store_temp_max_hyst, 1), + SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4), + SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5), + SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13), }; /* @@ -705,6 +864,9 @@ static int w83627ehf_detect(struct i2c_adapter *adapter) goto exit_detach; } + for (i = 0; i < 10; i++) + device_create_file_in(dev, i); + for (i = 0; i < 5; i++) { if (data->has_fan & (1 << i)) device_create_file_fan(dev, i); diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c new file mode 100644 index 00000000000..eec43abd57f --- /dev/null +++ b/drivers/hwmon/w83791d.c @@ -0,0 +1,1255 @@ +/* + w83791d.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + + Copyright (C) 2006 Charles Spirakis <bezaur@gmail.com> + + 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 following chips: + + Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + w83791d 10 5 3 3 0x71 0x5ca3 yes no + + The w83791d chip appears to be part way between the 83781d and the + 83792d. Thus, this file is derived from both the w83792d.c and + w83781d.c files, but its output is more along the lines of the + 83781d (which means there are no changes to the user-mode sensors + program which treats the 83791d as an 83781d). +*/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-vid.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> + +#define NUMBER_OF_VIN 10 +#define NUMBER_OF_FANIN 5 +#define NUMBER_OF_TEMPIN 3 + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(w83791d); +I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " + "{bus, clientaddr, subclientaddr1, subclientaddr2}"); + +static int reset; +module_param(reset, bool, 0); +MODULE_PARM_DESC(reset, "Set to one to force a hardware chip reset"); + +static int init; +module_param(init, bool, 0); +MODULE_PARM_DESC(init, "Set to one to force extra software initialization"); + +/* The W83791D registers */ +static const u8 W83791D_REG_IN[NUMBER_OF_VIN] = { + 0x20, /* VCOREA in DataSheet */ + 0x21, /* VINR0 in DataSheet */ + 0x22, /* +3.3VIN in DataSheet */ + 0x23, /* VDD5V in DataSheet */ + 0x24, /* +12VIN in DataSheet */ + 0x25, /* -12VIN in DataSheet */ + 0x26, /* -5VIN in DataSheet */ + 0xB0, /* 5VSB in DataSheet */ + 0xB1, /* VBAT in DataSheet */ + 0xB2 /* VINR1 in DataSheet */ +}; + +static const u8 W83791D_REG_IN_MAX[NUMBER_OF_VIN] = { + 0x2B, /* VCOREA High Limit in DataSheet */ + 0x2D, /* VINR0 High Limit in DataSheet */ + 0x2F, /* +3.3VIN High Limit in DataSheet */ + 0x31, /* VDD5V High Limit in DataSheet */ + 0x33, /* +12VIN High Limit in DataSheet */ + 0x35, /* -12VIN High Limit in DataSheet */ + 0x37, /* -5VIN High Limit in DataSheet */ + 0xB4, /* 5VSB High Limit in DataSheet */ + 0xB6, /* VBAT High Limit in DataSheet */ + 0xB8 /* VINR1 High Limit in DataSheet */ +}; +static const u8 W83791D_REG_IN_MIN[NUMBER_OF_VIN] = { + 0x2C, /* VCOREA Low Limit in DataSheet */ + 0x2E, /* VINR0 Low Limit in DataSheet */ + 0x30, /* +3.3VIN Low Limit in DataSheet */ + 0x32, /* VDD5V Low Limit in DataSheet */ + 0x34, /* +12VIN Low Limit in DataSheet */ + 0x36, /* -12VIN Low Limit in DataSheet */ + 0x38, /* -5VIN Low Limit in DataSheet */ + 0xB5, /* 5VSB Low Limit in DataSheet */ + 0xB7, /* VBAT Low Limit in DataSheet */ + 0xB9 /* VINR1 Low Limit in DataSheet */ +}; +static const u8 W83791D_REG_FAN[NUMBER_OF_FANIN] = { + 0x28, /* FAN 1 Count in DataSheet */ + 0x29, /* FAN 2 Count in DataSheet */ + 0x2A, /* FAN 3 Count in DataSheet */ + 0xBA, /* FAN 4 Count in DataSheet */ + 0xBB, /* FAN 5 Count in DataSheet */ +}; +static const u8 W83791D_REG_FAN_MIN[NUMBER_OF_FANIN] = { + 0x3B, /* FAN 1 Count Low Limit in DataSheet */ + 0x3C, /* FAN 2 Count Low Limit in DataSheet */ + 0x3D, /* FAN 3 Count Low Limit in DataSheet */ + 0xBC, /* FAN 4 Count Low Limit in DataSheet */ + 0xBD, /* FAN 5 Count Low Limit in DataSheet */ +}; + +static const u8 W83791D_REG_FAN_CFG[2] = { + 0x84, /* FAN 1/2 configuration */ + 0x95, /* FAN 3 configuration */ +}; + +static const u8 W83791D_REG_FAN_DIV[3] = { + 0x47, /* contains FAN1 and FAN2 Divisor */ + 0x4b, /* contains FAN3 Divisor */ + 0x5C, /* contains FAN4 and FAN5 Divisor */ +}; + +#define W83791D_REG_BANK 0x4E +#define W83791D_REG_TEMP2_CONFIG 0xC2 +#define W83791D_REG_TEMP3_CONFIG 0xCA + +static const u8 W83791D_REG_TEMP1[3] = { + 0x27, /* TEMP 1 in DataSheet */ + 0x39, /* TEMP 1 Over in DataSheet */ + 0x3A, /* TEMP 1 Hyst in DataSheet */ +}; + +static const u8 W83791D_REG_TEMP_ADD[2][6] = { + {0xC0, /* TEMP 2 in DataSheet */ + 0xC1, /* TEMP 2(0.5 deg) in DataSheet */ + 0xC5, /* TEMP 2 Over High part in DataSheet */ + 0xC6, /* TEMP 2 Over Low part in DataSheet */ + 0xC3, /* TEMP 2 Thyst High part in DataSheet */ + 0xC4}, /* TEMP 2 Thyst Low part in DataSheet */ + {0xC8, /* TEMP 3 in DataSheet */ + 0xC9, /* TEMP 3(0.5 deg) in DataSheet */ + 0xCD, /* TEMP 3 Over High part in DataSheet */ + 0xCE, /* TEMP 3 Over Low part in DataSheet */ + 0xCB, /* TEMP 3 Thyst High part in DataSheet */ + 0xCC} /* TEMP 3 Thyst Low part in DataSheet */ +}; + +#define W83791D_REG_BEEP_CONFIG 0x4D + +static const u8 W83791D_REG_BEEP_CTRL[3] = { + 0x56, /* BEEP Control Register 1 */ + 0x57, /* BEEP Control Register 2 */ + 0xA3, /* BEEP Control Register 3 */ +}; + +#define W83791D_REG_CONFIG 0x40 +#define W83791D_REG_VID_FANDIV 0x47 +#define W83791D_REG_DID_VID4 0x49 +#define W83791D_REG_WCHIPID 0x58 +#define W83791D_REG_CHIPMAN 0x4F +#define W83791D_REG_PIN 0x4B +#define W83791D_REG_I2C_SUBADDR 0x4A + +#define W83791D_REG_ALARM1 0xA9 /* realtime status register1 */ +#define W83791D_REG_ALARM2 0xAA /* realtime status register2 */ +#define W83791D_REG_ALARM3 0xAB /* realtime status register3 */ + +#define W83791D_REG_VBAT 0x5D +#define W83791D_REG_I2C_ADDR 0x48 + +/* The SMBus locks itself. The Winbond W83791D has a bank select register + (index 0x4e), but the driver only accesses registers in bank 0. Since + we don't switch banks, we don't need any special code to handle + locking access between bank switches */ +static inline int w83791d_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static inline int w83791d_write(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* The analog voltage inputs have 16mV LSB. Since the sysfs output is + in mV as would be measured on the chip input pin, need to just + multiply/divide by 16 to translate from/to register values. */ +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8) / 16), 0, 255)) +#define IN_FROM_REG(val) ((val) * 16) + +static u8 fan_to_reg(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); +} + +#define FAN_FROM_REG(val,div) ((val) == 0 ? -1 : \ + ((val) == 255 ? 0 : \ + 1350000 / ((val) * (div)))) + +/* for temp1 which is 8-bit resolution, LSB = 1 degree Celsius */ +#define TEMP1_FROM_REG(val) ((val) * 1000) +#define TEMP1_TO_REG(val) ((val) <= -128000 ? -128 : \ + (val) >= 127000 ? 127 : \ + (val) < 0 ? ((val) - 500) / 1000 : \ + ((val) + 500) / 1000) + +/* for temp2 and temp3 which are 9-bit resolution, LSB = 0.5 degree Celsius + Assumes the top 8 bits are the integral amount and the bottom 8 bits + are the fractional amount. Since we only have 0.5 degree resolution, + the bottom 7 bits will always be zero */ +#define TEMP23_FROM_REG(val) ((val) / 128 * 500) +#define TEMP23_TO_REG(val) ((val) <= -128000 ? 0x8000 : \ + (val) >= 127500 ? 0x7F80 : \ + (val) < 0 ? ((val) - 250) / 500 * 128 : \ + ((val) + 250) / 500 * 128) + + +#define BEEP_MASK_TO_REG(val) ((val) & 0xffffff) +#define BEEP_MASK_FROM_REG(val) ((val) & 0xffffff) + +#define DIV_FROM_REG(val) (1 << (val)) + +static u8 div_to_reg(int nr, long val) +{ + int i; + int max; + + /* first three fan's divisor max out at 8, rest max out at 128 */ + max = (nr < 3) ? 8 : 128; + val = SENSORS_LIMIT(val, 1, max) >> 1; + for (i = 0; i < 7; i++) { + if (val == 0) + break; + val >>= 1; + } + return (u8) i; +} + +struct w83791d_data { + struct i2c_client client; + struct class_device *class_dev; + struct mutex update_lock; + + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + /* array of 2 pointers to subclients */ + struct i2c_client *lm75[2]; + + /* volts */ + u8 in[NUMBER_OF_VIN]; /* Register value */ + u8 in_max[NUMBER_OF_VIN]; /* Register value */ + u8 in_min[NUMBER_OF_VIN]; /* Register value */ + + /* fans */ + u8 fan[NUMBER_OF_FANIN]; /* Register value */ + u8 fan_min[NUMBER_OF_FANIN]; /* Register value */ + u8 fan_div[NUMBER_OF_FANIN]; /* Register encoding, shifted right */ + + /* Temperature sensors */ + + s8 temp1[3]; /* current, over, thyst */ + s16 temp_add[2][3]; /* fixed point value. Top 8 bits are the + integral part, bottom 8 bits are the + fractional part. We only use the top + 9 bits as the resolution is only + to the 0.5 degree C... + two sensors with three values + (cur, over, hyst) */ + + /* Misc */ + u32 alarms; /* realtime status register encoding,combined */ + u8 beep_enable; /* Global beep enable */ + u32 beep_mask; /* Mask off specific beeps */ + u8 vid; /* Register encoding, combined */ + u8 vrm; /* hwmon-vid */ +}; + +static int w83791d_attach_adapter(struct i2c_adapter *adapter); +static int w83791d_detect(struct i2c_adapter *adapter, int address, int kind); +static int w83791d_detach_client(struct i2c_client *client); + +static int w83791d_read(struct i2c_client *client, u8 register); +static int w83791d_write(struct i2c_client *client, u8 register, u8 value); +static struct w83791d_data *w83791d_update_device(struct device *dev); + +#ifdef DEBUG +static void w83791d_print_debug(struct w83791d_data *data, struct device *dev); +#endif + +static void w83791d_init_client(struct i2c_client *client); + +static struct i2c_driver w83791d_driver = { + .driver = { + .name = "w83791d", + }, + .attach_adapter = w83791d_attach_adapter, + .detach_client = w83791d_detach_client, +}; + +/* following are the sysfs callback functions */ +#define show_in_reg(reg) \ +static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct sensor_device_attribute *sensor_attr = \ + to_sensor_dev_attr(attr); \ + struct w83791d_data *data = w83791d_update_device(dev); \ + int nr = sensor_attr->index; \ + return sprintf(buf,"%d\n", IN_FROM_REG(data->reg[nr])); \ +} + +show_in_reg(in); +show_in_reg(in_min); +show_in_reg(in_max); + +#define store_in_reg(REG, reg) \ +static ssize_t store_in_##reg(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct sensor_device_attribute *sensor_attr = \ + to_sensor_dev_attr(attr); \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83791d_data *data = i2c_get_clientdata(client); \ + unsigned long val = simple_strtoul(buf, NULL, 10); \ + int nr = sensor_attr->index; \ + \ + mutex_lock(&data->update_lock); \ + data->in_##reg[nr] = IN_TO_REG(val); \ + w83791d_write(client, W83791D_REG_IN_##REG[nr], data->in_##reg[nr]); \ + mutex_unlock(&data->update_lock); \ + \ + return count; \ +} +store_in_reg(MIN, min); +store_in_reg(MAX, max); + +static struct sensor_device_attribute sda_in_input[] = { + SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0), + SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1), + SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2), + SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3), + SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4), + SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5), + SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6), + SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7), + SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8), + SENSOR_ATTR(in9_input, S_IRUGO, show_in, NULL, 9), +}; + +static struct sensor_device_attribute sda_in_min[] = { + SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0), + SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1), + SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2), + SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 3), + SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 4), + SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 5), + SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 6), + SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 7), + SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 8), + SENSOR_ATTR(in9_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 9), +}; + +static struct sensor_device_attribute sda_in_max[] = { + SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0), + SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1), + SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2), + SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 3), + SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 4), + SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 5), + SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 6), + SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 7), + SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 8), + SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9), +}; + +#define show_fan_reg(reg) \ +static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct sensor_device_attribute *sensor_attr = \ + to_sensor_dev_attr(attr); \ + struct w83791d_data *data = w83791d_update_device(dev); \ + int nr = sensor_attr->index; \ + return sprintf(buf,"%d\n", \ + FAN_FROM_REG(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \ +} + +show_fan_reg(fan); +show_fan_reg(fan_min); + +static ssize_t store_fan_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + int nr = sensor_attr->index; + + mutex_lock(&data->update_lock); + data->fan_min[nr] = fan_to_reg(val, DIV_FROM_REG(data->fan_div[nr])); + w83791d_write(client, W83791D_REG_FAN_MIN[nr], data->fan_min[nr]); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%u\n", DIV_FROM_REG(data->fan_div[nr])); +} + +/* Note: we save and restore the fan minimum here, because its value is + determined in part by the fan divisor. This follows the principle of + least suprise; the user doesn't expect the fan minimum to change just + because the divisor changed. */ +static ssize_t store_fan_div(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int nr = sensor_attr->index; + unsigned long min; + u8 tmp_fan_div; + u8 fan_div_reg; + int indx = 0; + u8 keep_mask = 0; + u8 new_shift = 0; + + /* Save fan_min */ + min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])); + + mutex_lock(&data->update_lock); + data->fan_div[nr] = div_to_reg(nr, simple_strtoul(buf, NULL, 10)); + + switch (nr) { + case 0: + indx = 0; + keep_mask = 0xcf; + new_shift = 4; + break; + case 1: + indx = 0; + keep_mask = 0x3f; + new_shift = 6; + break; + case 2: + indx = 1; + keep_mask = 0x3f; + new_shift = 6; + break; + case 3: + indx = 2; + keep_mask = 0xf8; + new_shift = 0; + break; + case 4: + indx = 2; + keep_mask = 0x8f; + new_shift = 4; + break; +#ifdef DEBUG + default: + dev_warn(dev, "store_fan_div: Unexpected nr seen: %d\n", nr); + count = -EINVAL; + goto err_exit; +#endif + } + + fan_div_reg = w83791d_read(client, W83791D_REG_FAN_DIV[indx]) + & keep_mask; + tmp_fan_div = (data->fan_div[nr] << new_shift) & ~keep_mask; + + w83791d_write(client, W83791D_REG_FAN_DIV[indx], + fan_div_reg | tmp_fan_div); + + /* Restore fan_min */ + data->fan_min[nr] = fan_to_reg(min, DIV_FROM_REG(data->fan_div[nr])); + w83791d_write(client, W83791D_REG_FAN_MIN[nr], data->fan_min[nr]); + +#ifdef DEBUG +err_exit: +#endif + mutex_unlock(&data->update_lock); + + return count; +} + +static struct sensor_device_attribute sda_fan_input[] = { + SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0), + SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1), + SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2), + SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3), + SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4), +}; + +static struct sensor_device_attribute sda_fan_min[] = { + SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, + show_fan_min, store_fan_min, 0), + SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, + show_fan_min, store_fan_min, 1), + SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, + show_fan_min, store_fan_min, 2), + SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO, + show_fan_min, store_fan_min, 3), + SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO, + show_fan_min, store_fan_min, 4), +}; + +static struct sensor_device_attribute sda_fan_div[] = { + SENSOR_ATTR(fan1_div, S_IWUSR | S_IRUGO, + show_fan_div, store_fan_div, 0), + SENSOR_ATTR(fan2_div, S_IWUSR | S_IRUGO, + show_fan_div, store_fan_div, 1), + SENSOR_ATTR(fan3_div, S_IWUSR | S_IRUGO, + show_fan_div, store_fan_div, 2), + SENSOR_ATTR(fan4_div, S_IWUSR | S_IRUGO, + show_fan_div, store_fan_div, 3), + SENSOR_ATTR(fan5_div, S_IWUSR | S_IRUGO, + show_fan_div, store_fan_div, 4), +}; + +/* read/write the temperature1, includes measured value and limits */ +static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp1[attr->index])); +} + +static ssize_t store_temp1(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + int nr = attr->index; + + mutex_lock(&data->update_lock); + data->temp1[nr] = TEMP1_TO_REG(val); + w83791d_write(client, W83791D_REG_TEMP1[nr], data->temp1[nr]); + mutex_unlock(&data->update_lock); + return count; +} + +/* read/write temperature2-3, includes measured value and limits */ +static ssize_t show_temp23(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct w83791d_data *data = w83791d_update_device(dev); + int nr = attr->nr; + int index = attr->index; + return sprintf(buf, "%d\n", TEMP23_FROM_REG(data->temp_add[nr][index])); +} + +static ssize_t store_temp23(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + int nr = attr->nr; + int index = attr->index; + + mutex_lock(&data->update_lock); + data->temp_add[nr][index] = TEMP23_TO_REG(val); + w83791d_write(client, W83791D_REG_TEMP_ADD[nr][index * 2], + data->temp_add[nr][index] >> 8); + w83791d_write(client, W83791D_REG_TEMP_ADD[nr][index * 2 + 1], + data->temp_add[nr][index] & 0x80); + mutex_unlock(&data->update_lock); + + return count; +} + +static struct sensor_device_attribute_2 sda_temp_input[] = { + SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp1, NULL, 0, 0), + SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp23, NULL, 0, 0), + SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp23, NULL, 1, 0), +}; + +static struct sensor_device_attribute_2 sda_temp_max[] = { + SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, + show_temp1, store_temp1, 0, 1), + SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, + show_temp23, store_temp23, 0, 1), + SENSOR_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, + show_temp23, store_temp23, 1, 1), +}; + +static struct sensor_device_attribute_2 sda_temp_max_hyst[] = { + SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR, + show_temp1, store_temp1, 0, 2), + SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR, + show_temp23, store_temp23, 0, 2), + SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR, + show_temp23, store_temp23, 1, 2), +}; + + +/* get reatime status of all sensors items: voltage, temp, fan */ +static ssize_t show_alarms_reg(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%u\n", data->alarms); +} + +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); + +/* Beep control */ + +#define GLOBAL_BEEP_ENABLE_SHIFT 15 +#define GLOBAL_BEEP_ENABLE_MASK (1 << GLOBAL_BEEP_ENABLE_SHIFT) + +static ssize_t show_beep_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%d\n", data->beep_enable); +} + +static ssize_t show_beep_mask(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%d\n", BEEP_MASK_FROM_REG(data->beep_mask)); +} + + +static ssize_t store_beep_mask(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + int i; + + mutex_lock(&data->update_lock); + + /* The beep_enable state overrides any enabling request from + the masks */ + data->beep_mask = BEEP_MASK_TO_REG(val) & ~GLOBAL_BEEP_ENABLE_MASK; + data->beep_mask |= (data->beep_enable << GLOBAL_BEEP_ENABLE_SHIFT); + + val = data->beep_mask; + + for (i = 0; i < 3; i++) { + w83791d_write(client, W83791D_REG_BEEP_CTRL[i], (val & 0xff)); + val >>= 8; + } + + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t store_beep_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + mutex_lock(&data->update_lock); + + data->beep_enable = val ? 1 : 0; + + /* Keep the full mask value in sync with the current enable */ + data->beep_mask &= ~GLOBAL_BEEP_ENABLE_MASK; + data->beep_mask |= (data->beep_enable << GLOBAL_BEEP_ENABLE_SHIFT); + + /* The global control is in the second beep control register + so only need to update that register */ + val = (data->beep_mask >> 8) & 0xff; + + w83791d_write(client, W83791D_REG_BEEP_CTRL[1], val); + + mutex_unlock(&data->update_lock); + + return count; +} + +static struct sensor_device_attribute sda_beep_ctrl[] = { + SENSOR_ATTR(beep_enable, S_IRUGO | S_IWUSR, + show_beep_enable, store_beep_enable, 0), + SENSOR_ATTR(beep_mask, S_IRUGO | S_IWUSR, + show_beep_mask, store_beep_mask, 1) +}; + +/* cpu voltage regulation information */ +static ssize_t show_vid_reg(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); +} + +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); + +static ssize_t show_vrm_reg(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct w83791d_data *data = w83791d_update_device(dev); + return sprintf(buf, "%d\n", data->vrm); +} + +static ssize_t store_vrm_reg(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + /* No lock needed as vrm is internal to the driver + (not read from a chip register) and so is not + updated in w83791d_update_device() */ + data->vrm = val; + + return count; +} + +static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); + +/* This function is called when: + * w83791d_driver is inserted (when this module is loaded), for each + available adapter + * when a new adapter is inserted (and w83791d_driver is still present) */ +static int w83791d_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_probe(adapter, &addr_data, w83791d_detect); +} + + +static int w83791d_create_subclient(struct i2c_adapter *adapter, + struct i2c_client *client, int addr, + struct i2c_client **sub_cli) +{ + int err; + struct i2c_client *sub_client; + + (*sub_cli) = sub_client = + kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (!(sub_client)) { + return -ENOMEM; + } + sub_client->addr = 0x48 + addr; + i2c_set_clientdata(sub_client, NULL); + sub_client->adapter = adapter; + sub_client->driver = &w83791d_driver; + strlcpy(sub_client->name, "w83791d subclient", I2C_NAME_SIZE); + if ((err = i2c_attach_client(sub_client))) { + dev_err(&client->dev, "subclient registration " + "at address 0x%x failed\n", sub_client->addr); + kfree(sub_client); + return err; + } + return 0; +} + + +static int w83791d_detect_subclients(struct i2c_adapter *adapter, int address, + int kind, struct i2c_client *client) +{ + struct w83791d_data *data = i2c_get_clientdata(client); + int i, id, err; + u8 val; + + id = i2c_adapter_id(adapter); + if (force_subclients[0] == id && force_subclients[1] == address) { + for (i = 2; i <= 3; i++) { + if (force_subclients[i] < 0x48 || + force_subclients[i] > 0x4f) { + dev_err(&client->dev, + "invalid subclient " + "address %d; must be 0x48-0x4f\n", + force_subclients[i]); + err = -ENODEV; + goto error_sc_0; + } + } + w83791d_write(client, W83791D_REG_I2C_SUBADDR, + (force_subclients[2] & 0x07) | + ((force_subclients[3] & 0x07) << 4)); + } + + val = w83791d_read(client, W83791D_REG_I2C_SUBADDR); + if (!(val & 0x08)) { + err = w83791d_create_subclient(adapter, client, + val & 0x7, &data->lm75[0]); + if (err < 0) + goto error_sc_0; + } + if (!(val & 0x80)) { + if ((data->lm75[0] != NULL) && + ((val & 0x7) == ((val >> 4) & 0x7))) { + dev_err(&client->dev, + "duplicate addresses 0x%x, " + "use force_subclient\n", + data->lm75[0]->addr); + err = -ENODEV; + goto error_sc_1; + } + err = w83791d_create_subclient(adapter, client, + (val >> 4) & 0x7, &data->lm75[1]); + if (err < 0) + goto error_sc_1; + } + + return 0; + +/* Undo inits in case of errors */ + +error_sc_1: + if (data->lm75[0] != NULL) { + i2c_detach_client(data->lm75[0]); + kfree(data->lm75[0]); + } +error_sc_0: + return err; +} + + +static int w83791d_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct device *dev; + struct w83791d_data *data; + int i, val1, val2; + int err = 0; + const char *client_name = ""; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + goto error0; + } + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access w83791d_{read,write}_value. */ + if (!(data = kzalloc(sizeof(struct w83791d_data), GFP_KERNEL))) { + err = -ENOMEM; + goto error0; + } + + client = &data->client; + dev = &client->dev; + i2c_set_clientdata(client, data); + client->addr = address; + client->adapter = adapter; + client->driver = &w83791d_driver; + mutex_init(&data->update_lock); + + /* Now, we do the remaining detection. */ + + /* The w83791d may be stuck in some other bank than bank 0. This may + make reading other information impossible. Specify a force=... + parameter, and the Winbond will be reset to the right bank. */ + if (kind < 0) { + if (w83791d_read(client, W83791D_REG_CONFIG) & 0x80) { + dev_dbg(dev, "Detection failed at step 1\n"); + goto error1; + } + val1 = w83791d_read(client, W83791D_REG_BANK); + val2 = w83791d_read(client, W83791D_REG_CHIPMAN); + /* Check for Winbond ID if in bank 0 */ + if (!(val1 & 0x07)) { + /* yes it is Bank0 */ + if (((!(val1 & 0x80)) && (val2 != 0xa3)) || + ((val1 & 0x80) && (val2 != 0x5c))) { + dev_dbg(dev, "Detection failed at step 2\n"); + goto error1; + } + } + /* If Winbond chip, address of chip and W83791D_REG_I2C_ADDR + should match */ + if (w83791d_read(client, W83791D_REG_I2C_ADDR) != address) { + dev_dbg(dev, "Detection failed at step 3\n"); + goto error1; + } + } + + /* We either have a force parameter or we have reason to + believe it is a Winbond chip. Either way, we want bank 0 and + Vendor ID high byte */ + val1 = w83791d_read(client, W83791D_REG_BANK) & 0x78; + w83791d_write(client, W83791D_REG_BANK, val1 | 0x80); + + /* Verify it is a Winbond w83791d */ + if (kind <= 0) { + /* get vendor ID */ + val2 = w83791d_read(client, W83791D_REG_CHIPMAN); + if (val2 != 0x5c) { /* the vendor is NOT Winbond */ + dev_dbg(dev, "Detection failed at step 4\n"); + goto error1; + } + val1 = w83791d_read(client, W83791D_REG_WCHIPID); + if (val1 == 0x71) { + kind = w83791d; + } else { + if (kind == 0) + dev_warn(dev, + "w83791d: Ignoring 'force' parameter " + "for unknown chip at adapter %d, " + "address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto error1; + } + } + + if (kind == w83791d) { + client_name = "w83791d"; + } else { + dev_err(dev, "w83791d: Internal error: unknown kind (%d)?!?", + kind); + goto error1; + } + +#ifdef DEBUG + val1 = w83791d_read(client, W83791D_REG_DID_VID4); + dev_dbg(dev, "Device ID version: %d.%d (0x%02x)\n", + (val1 >> 5) & 0x07, (val1 >> 1) & 0x0f, val1); +#endif + + /* Fill in the remaining client fields and put into the global list */ + strlcpy(client->name, client_name, I2C_NAME_SIZE); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(client))) + goto error1; + + if ((err = w83791d_detect_subclients(adapter, address, kind, client))) + goto error2; + + /* Initialize the chip */ + w83791d_init_client(client); + + /* If the fan_div is changed, make sure there is a rational + fan_min in place */ + for (i = 0; i < NUMBER_OF_FANIN; i++) { + data->fan_min[i] = w83791d_read(client, W83791D_REG_FAN_MIN[i]); + } + + /* Register sysfs hooks */ + data->class_dev = hwmon_device_register(dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto error3; + } + + for (i = 0; i < NUMBER_OF_VIN; i++) { + device_create_file(dev, &sda_in_input[i].dev_attr); + device_create_file(dev, &sda_in_min[i].dev_attr); + device_create_file(dev, &sda_in_max[i].dev_attr); + } + + for (i = 0; i < NUMBER_OF_FANIN; i++) { + device_create_file(dev, &sda_fan_input[i].dev_attr); + device_create_file(dev, &sda_fan_div[i].dev_attr); + device_create_file(dev, &sda_fan_min[i].dev_attr); + } + + for (i = 0; i < NUMBER_OF_TEMPIN; i++) { + device_create_file(dev, &sda_temp_input[i].dev_attr); + device_create_file(dev, &sda_temp_max[i].dev_attr); + device_create_file(dev, &sda_temp_max_hyst[i].dev_attr); + } + + device_create_file(dev, &dev_attr_alarms); + + for (i = 0; i < ARRAY_SIZE(sda_beep_ctrl); i++) { + device_create_file(dev, &sda_beep_ctrl[i].dev_attr); + } + + device_create_file(dev, &dev_attr_cpu0_vid); + device_create_file(dev, &dev_attr_vrm); + + return 0; + +error3: + if (data->lm75[0] != NULL) { + i2c_detach_client(data->lm75[0]); + kfree(data->lm75[0]); + } + if (data->lm75[1] != NULL) { + i2c_detach_client(data->lm75[1]); + kfree(data->lm75[1]); + } +error2: + i2c_detach_client(client); +error1: + kfree(data); +error0: + return err; +} + +static int w83791d_detach_client(struct i2c_client *client) +{ + struct w83791d_data *data = i2c_get_clientdata(client); + int err; + + /* main client */ + if (data) + hwmon_device_unregister(data->class_dev); + + if ((err = i2c_detach_client(client))) + return err; + + /* main client */ + if (data) + kfree(data); + /* subclient */ + else + kfree(client); + + return 0; +} + +static void w83791d_init_client(struct i2c_client *client) +{ + struct w83791d_data *data = i2c_get_clientdata(client); + u8 tmp; + u8 old_beep; + + /* The difference between reset and init is that reset + does a hard reset of the chip via index 0x40, bit 7, + but init simply forces certain registers to have "sane" + values. The hope is that the BIOS has done the right + thing (which is why the default is reset=0, init=0), + but if not, reset is the hard hammer and init + is the soft mallet both of which are trying to whack + things into place... + NOTE: The data sheet makes a distinction between + "power on defaults" and "reset by MR". As far as I can tell, + the hard reset puts everything into a power-on state so I'm + not sure what "reset by MR" means or how it can happen. + */ + if (reset || init) { + /* keep some BIOS settings when we... */ + old_beep = w83791d_read(client, W83791D_REG_BEEP_CONFIG); + + if (reset) { + /* ... reset the chip and ... */ + w83791d_write(client, W83791D_REG_CONFIG, 0x80); + } + + /* ... disable power-on abnormal beep */ + w83791d_write(client, W83791D_REG_BEEP_CONFIG, old_beep | 0x80); + + /* disable the global beep (not done by hard reset) */ + tmp = w83791d_read(client, W83791D_REG_BEEP_CTRL[1]); + w83791d_write(client, W83791D_REG_BEEP_CTRL[1], tmp & 0xef); + + if (init) { + /* Make sure monitoring is turned on for add-ons */ + tmp = w83791d_read(client, W83791D_REG_TEMP2_CONFIG); + if (tmp & 1) { + w83791d_write(client, W83791D_REG_TEMP2_CONFIG, + tmp & 0xfe); + } + + tmp = w83791d_read(client, W83791D_REG_TEMP3_CONFIG); + if (tmp & 1) { + w83791d_write(client, W83791D_REG_TEMP3_CONFIG, + tmp & 0xfe); + } + + /* Start monitoring */ + tmp = w83791d_read(client, W83791D_REG_CONFIG) & 0xf7; + w83791d_write(client, W83791D_REG_CONFIG, tmp | 0x01); + } + } + + data->vrm = vid_which_vrm(); +} + +static struct w83791d_data *w83791d_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83791d_data *data = i2c_get_clientdata(client); + int i, j; + u8 reg_array_tmp[3]; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + (HZ * 3)) + || !data->valid) { + dev_dbg(dev, "Starting w83791d device update\n"); + + /* Update the voltages measured value and limits */ + for (i = 0; i < NUMBER_OF_VIN; i++) { + data->in[i] = w83791d_read(client, + W83791D_REG_IN[i]); + data->in_max[i] = w83791d_read(client, + W83791D_REG_IN_MAX[i]); + data->in_min[i] = w83791d_read(client, + W83791D_REG_IN_MIN[i]); + } + + /* Update the fan counts and limits */ + for (i = 0; i < NUMBER_OF_FANIN; i++) { + /* Update the Fan measured value and limits */ + data->fan[i] = w83791d_read(client, + W83791D_REG_FAN[i]); + data->fan_min[i] = w83791d_read(client, + W83791D_REG_FAN_MIN[i]); + } + + /* Update the fan divisor */ + for (i = 0; i < 3; i++) { + reg_array_tmp[i] = w83791d_read(client, + W83791D_REG_FAN_DIV[i]); + } + data->fan_div[0] = (reg_array_tmp[0] >> 4) & 0x03; + data->fan_div[1] = (reg_array_tmp[0] >> 6) & 0x03; + data->fan_div[2] = (reg_array_tmp[1] >> 6) & 0x03; + data->fan_div[3] = reg_array_tmp[2] & 0x07; + data->fan_div[4] = (reg_array_tmp[2] >> 4) & 0x07; + + /* Update the first temperature sensor */ + for (i = 0; i < 3; i++) { + data->temp1[i] = w83791d_read(client, + W83791D_REG_TEMP1[i]); + } + + /* Update the rest of the temperature sensors */ + for (i = 0; i < 2; i++) { + for (j = 0; j < 3; j++) { + data->temp_add[i][j] = + (w83791d_read(client, + W83791D_REG_TEMP_ADD[i][j * 2]) << 8) | + w83791d_read(client, + W83791D_REG_TEMP_ADD[i][j * 2 + 1]); + } + } + + /* Update the realtime status */ + data->alarms = + w83791d_read(client, W83791D_REG_ALARM1) + + (w83791d_read(client, W83791D_REG_ALARM2) << 8) + + (w83791d_read(client, W83791D_REG_ALARM3) << 16); + + /* Update the beep configuration information */ + data->beep_mask = + w83791d_read(client, W83791D_REG_BEEP_CTRL[0]) + + (w83791d_read(client, W83791D_REG_BEEP_CTRL[1]) << 8) + + (w83791d_read(client, W83791D_REG_BEEP_CTRL[2]) << 16); + + data->beep_enable = + (data->beep_mask >> GLOBAL_BEEP_ENABLE_SHIFT) & 0x01; + + /* Update the cpu voltage information */ + i = w83791d_read(client, W83791D_REG_VID_FANDIV); + data->vid = i & 0x0f; + data->vid |= (w83791d_read(client, W83791D_REG_DID_VID4) & 0x01) + << 4; + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + +#ifdef DEBUG + w83791d_print_debug(data, dev); +#endif + + return data; +} + +#ifdef DEBUG +static void w83791d_print_debug(struct w83791d_data *data, struct device *dev) +{ + int i = 0, j = 0; + + dev_dbg(dev, "======Start of w83791d debug values======\n"); + dev_dbg(dev, "%d set of Voltages: ===>\n", NUMBER_OF_VIN); + for (i = 0; i < NUMBER_OF_VIN; i++) { + dev_dbg(dev, "vin[%d] is: 0x%02x\n", i, data->in[i]); + dev_dbg(dev, "vin[%d] min is: 0x%02x\n", i, data->in_min[i]); + dev_dbg(dev, "vin[%d] max is: 0x%02x\n", i, data->in_max[i]); + } + dev_dbg(dev, "%d set of Fan Counts/Divisors: ===>\n", NUMBER_OF_FANIN); + for (i = 0; i < NUMBER_OF_FANIN; i++) { + dev_dbg(dev, "fan[%d] is: 0x%02x\n", i, data->fan[i]); + dev_dbg(dev, "fan[%d] min is: 0x%02x\n", i, data->fan_min[i]); + dev_dbg(dev, "fan_div[%d] is: 0x%02x\n", i, data->fan_div[i]); + } + + /* temperature math is signed, but only print out the + bits that matter */ + dev_dbg(dev, "%d set of Temperatures: ===>\n", NUMBER_OF_TEMPIN); + for (i = 0; i < 3; i++) { + dev_dbg(dev, "temp1[%d] is: 0x%02x\n", i, (u8) data->temp1[i]); + } + for (i = 0; i < 2; i++) { + for (j = 0; j < 3; j++) { + dev_dbg(dev, "temp_add[%d][%d] is: 0x%04x\n", i, j, + (u16) data->temp_add[i][j]); + } + } + + dev_dbg(dev, "Misc Information: ===>\n"); + dev_dbg(dev, "alarm is: 0x%08x\n", data->alarms); + dev_dbg(dev, "beep_mask is: 0x%08x\n", data->beep_mask); + dev_dbg(dev, "beep_enable is: %d\n", data->beep_enable); + dev_dbg(dev, "vid is: 0x%02x\n", data->vid); + dev_dbg(dev, "vrm is: 0x%02x\n", data->vrm); + dev_dbg(dev, "=======End of w83791d debug values========\n"); + dev_dbg(dev, "\n"); +} +#endif + +static int __init sensors_w83791d_init(void) +{ + return i2c_add_driver(&w83791d_driver); +} + +static void __exit sensors_w83791d_exit(void) +{ + i2c_del_driver(&w83791d_driver); +} + +MODULE_AUTHOR("Charles Spirakis <bezaur@gmail.com>"); +MODULE_DESCRIPTION("W83791D driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_w83791d_init); +module_exit(sensors_w83791d_exit); diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index 958602e2841..4ef884c216e 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -250,8 +250,6 @@ FAN_TO_REG(long rpm, int div) : (val)) / 1000, 0, 0xff)) #define TEMP_ADD_TO_REG_LOW(val) ((val%1000) ? 0x80 : 0x00) -#define PWM_FROM_REG(val) (val) -#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255)) #define DIV_FROM_REG(val) (1 << (val)) static inline u8 @@ -291,7 +289,6 @@ struct w83792d_data { u8 pwm[7]; /* We only consider the first 3 set of pwm, although 792 chip has 7 set of pwm. */ u8 pwmenable[3]; - u8 pwm_mode[7]; /* indicates PWM or DC mode: 1->PWM; 0->DC */ u32 alarms; /* realtime status register encoding,combined */ u8 chassis; /* Chassis status */ u8 chassis_clear; /* CLR_CHS, clear chassis intrusion detection */ @@ -375,8 +372,10 @@ static ssize_t store_in_##reg (struct device *dev, \ u32 val; \ \ val = simple_strtoul(buf, NULL, 10); \ + mutex_lock(&data->update_lock); \ data->in_##reg[nr] = SENSORS_LIMIT(IN_TO_REG(nr, val)/4, 0, 255); \ w83792d_write_value(client, W83792D_REG_IN_##REG[nr], data->in_##reg[nr]); \ + mutex_unlock(&data->update_lock); \ \ return count; \ } @@ -443,9 +442,11 @@ store_fan_min(struct device *dev, struct device_attribute *attr, u32 val; val = simple_strtoul(buf, NULL, 10); + mutex_lock(&data->update_lock); data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); w83792d_write_value(client, W83792D_REG_FAN_MIN[nr], data->fan_min[nr]); + mutex_unlock(&data->update_lock); return count; } @@ -478,6 +479,7 @@ store_fan_div(struct device *dev, struct device_attribute *attr, u8 tmp_fan_div; /* Save fan_min */ + mutex_lock(&data->update_lock); min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])); @@ -493,6 +495,7 @@ store_fan_div(struct device *dev, struct device_attribute *attr, /* Restore fan_min */ data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); w83792d_write_value(client, W83792D_REG_FAN_MIN[nr], data->fan_min[nr]); + mutex_unlock(&data->update_lock); return count; } @@ -547,10 +550,11 @@ static ssize_t store_temp1(struct device *dev, struct device_attribute *attr, s32 val; val = simple_strtol(buf, NULL, 10); - + mutex_lock(&data->update_lock); data->temp1[nr] = TEMP1_TO_REG(val); w83792d_write_value(client, W83792D_REG_TEMP1[nr], data->temp1[nr]); + mutex_unlock(&data->update_lock); return count; } @@ -580,13 +584,14 @@ static ssize_t store_temp23(struct device *dev, struct device_attribute *attr, s32 val; val = simple_strtol(buf, NULL, 10); - + mutex_lock(&data->update_lock); data->temp_add[nr][index] = TEMP_ADD_TO_REG_HIGH(val); data->temp_add[nr][index+1] = TEMP_ADD_TO_REG_LOW(val); w83792d_write_value(client, W83792D_REG_TEMP_ADD[nr][index], data->temp_add[nr][index]); w83792d_write_value(client, W83792D_REG_TEMP_ADD[nr][index+1], data->temp_add[nr][index+1]); + mutex_unlock(&data->update_lock); return count; } @@ -627,7 +632,7 @@ show_pwm(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct w83792d_data *data = w83792d_update_device(dev); - return sprintf(buf, "%ld\n", (long) PWM_FROM_REG(data->pwm[nr-1])); + return sprintf(buf, "%d\n", (data->pwm[nr] & 0x0f) << 4); } static ssize_t @@ -659,14 +664,16 @@ store_pwm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index - 1; + int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct w83792d_data *data = i2c_get_clientdata(client); - u32 val; + u8 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255) >> 4; - val = simple_strtoul(buf, NULL, 10); - data->pwm[nr] = PWM_TO_REG(val); + mutex_lock(&data->update_lock); + val |= w83792d_read_value(client, W83792D_REG_PWM[nr]) & 0xf0; + data->pwm[nr] = val; w83792d_write_value(client, W83792D_REG_PWM[nr], data->pwm[nr]); + mutex_unlock(&data->update_lock); return count; } @@ -683,6 +690,10 @@ store_pwmenable(struct device *dev, struct device_attribute *attr, u8 fan_cfg_tmp, cfg1_tmp, cfg2_tmp, cfg3_tmp, cfg4_tmp; val = simple_strtoul(buf, NULL, 10); + if (val < 1 || val > 3) + return -EINVAL; + + mutex_lock(&data->update_lock); switch (val) { case 1: data->pwmenable[nr] = 0; /* manual mode */ @@ -693,8 +704,6 @@ store_pwmenable(struct device *dev, struct device_attribute *attr, case 3: data->pwmenable[nr] = 1; /* thermal cruise/Smart Fan I */ break; - default: - return -EINVAL; } cfg1_tmp = data->pwmenable[0]; cfg2_tmp = (data->pwmenable[1]) << 2; @@ -702,14 +711,15 @@ store_pwmenable(struct device *dev, struct device_attribute *attr, cfg4_tmp = w83792d_read_value(client,W83792D_REG_FAN_CFG) & 0xc0; fan_cfg_tmp = ((cfg4_tmp | cfg3_tmp) | cfg2_tmp) | cfg1_tmp; w83792d_write_value(client, W83792D_REG_FAN_CFG, fan_cfg_tmp); + mutex_unlock(&data->update_lock); return count; } static struct sensor_device_attribute sda_pwm[] = { - SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1), - SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2), - SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3), + SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0), + SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1), + SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2), }; static struct sensor_device_attribute sda_pwm_enable[] = { SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, @@ -728,7 +738,7 @@ show_pwm_mode(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; struct w83792d_data *data = w83792d_update_device(dev); - return sprintf(buf, "%d\n", data->pwm_mode[nr-1]); + return sprintf(buf, "%d\n", data->pwm[nr] >> 7); } static ssize_t @@ -736,29 +746,35 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index - 1; + int nr = sensor_attr->index; struct i2c_client *client = to_i2c_client(dev); struct w83792d_data *data = i2c_get_clientdata(client); u32 val; - u8 pwm_mode_mask = 0; val = simple_strtoul(buf, NULL, 10); - data->pwm_mode[nr] = SENSORS_LIMIT(val, 0, 1); - pwm_mode_mask = w83792d_read_value(client, - W83792D_REG_PWM[nr]) & 0x7f; - w83792d_write_value(client, W83792D_REG_PWM[nr], - ((data->pwm_mode[nr]) << 7) | pwm_mode_mask); + if (val != 0 && val != 1) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->pwm[nr] = w83792d_read_value(client, W83792D_REG_PWM[nr]); + if (val) { /* PWM mode */ + data->pwm[nr] |= 0x80; + } else { /* DC mode */ + data->pwm[nr] &= 0x7f; + } + w83792d_write_value(client, W83792D_REG_PWM[nr], data->pwm[nr]); + mutex_unlock(&data->update_lock); return count; } static struct sensor_device_attribute sda_pwm_mode[] = { SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, - show_pwm_mode, store_pwm_mode, 1), + show_pwm_mode, store_pwm_mode, 0), SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, - show_pwm_mode, store_pwm_mode, 2), + show_pwm_mode, store_pwm_mode, 1), SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, - show_pwm_mode, store_pwm_mode, 3), + show_pwm_mode, store_pwm_mode, 2), }; @@ -789,12 +805,13 @@ store_chassis_clear(struct device *dev, struct device_attribute *attr, u8 temp1 = 0, temp2 = 0; val = simple_strtoul(buf, NULL, 10); - + mutex_lock(&data->update_lock); data->chassis_clear = SENSORS_LIMIT(val, 0 ,1); temp1 = ((data->chassis_clear) << 7) & 0x80; temp2 = w83792d_read_value(client, W83792D_REG_CHASSIS_CLR) & 0x7f; w83792d_write_value(client, W83792D_REG_CHASSIS_CLR, temp1 | temp2); + mutex_unlock(&data->update_lock); return count; } @@ -827,10 +844,12 @@ store_thermal_cruise(struct device *dev, struct device_attribute *attr, val = simple_strtoul(buf, NULL, 10); target_tmp = val; target_tmp = target_tmp & 0x7f; + mutex_lock(&data->update_lock); target_mask = w83792d_read_value(client, W83792D_REG_THERMAL[nr]) & 0x80; data->thermal_cruise[nr] = SENSORS_LIMIT(target_tmp, 0, 255); w83792d_write_value(client, W83792D_REG_THERMAL[nr], (data->thermal_cruise[nr]) | target_mask); + mutex_unlock(&data->update_lock); return count; } @@ -867,6 +886,7 @@ store_tolerance(struct device *dev, struct device_attribute *attr, u8 tol_tmp, tol_mask; val = simple_strtoul(buf, NULL, 10); + mutex_lock(&data->update_lock); tol_mask = w83792d_read_value(client, W83792D_REG_TOLERANCE[nr]) & ((nr == 1) ? 0x0f : 0xf0); tol_tmp = SENSORS_LIMIT(val, 0, 15); @@ -877,6 +897,7 @@ store_tolerance(struct device *dev, struct device_attribute *attr, } w83792d_write_value(client, W83792D_REG_TOLERANCE[nr], tol_mask | tol_tmp); + mutex_unlock(&data->update_lock); return count; } @@ -915,11 +936,13 @@ store_sf2_point(struct device *dev, struct device_attribute *attr, u8 mask_tmp = 0; val = simple_strtoul(buf, NULL, 10); + mutex_lock(&data->update_lock); data->sf2_points[index][nr] = SENSORS_LIMIT(val, 0, 127); mask_tmp = w83792d_read_value(client, W83792D_REG_POINTS[index][nr]) & 0x80; w83792d_write_value(client, W83792D_REG_POINTS[index][nr], mask_tmp|data->sf2_points[index][nr]); + mutex_unlock(&data->update_lock); return count; } @@ -979,6 +1002,7 @@ store_sf2_level(struct device *dev, struct device_attribute *attr, u8 mask_tmp=0, level_tmp=0; val = simple_strtoul(buf, NULL, 10); + mutex_lock(&data->update_lock); data->sf2_levels[index][nr] = SENSORS_LIMIT((val * 15) / 100, 0, 15); mask_tmp = w83792d_read_value(client, W83792D_REG_LEVELS[index][nr]) & ((nr==3) ? 0xf0 : 0x0f); @@ -988,6 +1012,7 @@ store_sf2_level(struct device *dev, struct device_attribute *attr, level_tmp = data->sf2_levels[index][nr] << 4; } w83792d_write_value(client, W83792D_REG_LEVELS[index][nr], level_tmp | mask_tmp); + mutex_unlock(&data->update_lock); return count; } @@ -1373,7 +1398,7 @@ static struct w83792d_data *w83792d_update_device(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct w83792d_data *data = i2c_get_clientdata(client); int i, j; - u8 reg_array_tmp[4], pwm_array_tmp[7], reg_tmp; + u8 reg_array_tmp[4], reg_tmp; mutex_lock(&data->update_lock); @@ -1402,10 +1427,8 @@ static struct w83792d_data *w83792d_update_device(struct device *dev) data->fan_min[i] = w83792d_read_value(client, W83792D_REG_FAN_MIN[i]); /* Update the PWM/DC Value and PWM/DC flag */ - pwm_array_tmp[i] = w83792d_read_value(client, + data->pwm[i] = w83792d_read_value(client, W83792D_REG_PWM[i]); - data->pwm[i] = pwm_array_tmp[i] & 0x0f; - data->pwm_mode[i] = pwm_array_tmp[i] >> 7; } reg_tmp = w83792d_read_value(client, W83792D_REG_FAN_CFG); @@ -1513,7 +1536,6 @@ static void w83792d_print_debug(struct w83792d_data *data, struct device *dev) dev_dbg(dev, "fan[%d] is: 0x%x\n", i, data->fan[i]); dev_dbg(dev, "fan[%d] min is: 0x%x\n", i, data->fan_min[i]); dev_dbg(dev, "pwm[%d] is: 0x%x\n", i, data->pwm[i]); - dev_dbg(dev, "pwm_mode[%d] is: 0x%x\n", i, data->pwm_mode[i]); } dev_dbg(dev, "3 set of Temperatures: =====>\n"); for (i=0; i<3; i++) { diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index d6d44946a28..884320e7040 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -163,7 +163,7 @@ config I2C_PXA_SLAVE I2C bus. config I2C_PIIX4 - tristate "Intel PIIX4" + tristate "Intel PIIX4 and compatible (ATI/Serverworks/Broadcom/SMSC)" depends on I2C && PCI help If you say yes to this option, support will be included for the Intel @@ -172,6 +172,9 @@ config I2C_PIIX4 of Broadcom): Intel PIIX4 Intel 440MX + ATI IXP200 + ATI IXP300 + ATI IXP400 Serverworks OSB4 Serverworks CSB5 Serverworks CSB6 @@ -252,12 +255,12 @@ config I2C_POWERMAC will be called i2c-powermac. config I2C_MPC - tristate "MPC107/824x/85xx/52xx" + tristate "MPC107/824x/85xx/52xx/86xx" depends on I2C && PPC32 help If you say yes to this option, support will be included for the built-in I2C interface on the MPC107/Tsi107/MPC8240/MPC8245 and - MPC85xx family processors. The driver may also work on 52xx + MPC85xx/MPC8641 family processors. The driver may also work on 52xx family processors, though interrupts are known not to work. This driver can also be built as a module. If so, the module @@ -273,6 +276,17 @@ config I2C_NFORCE2 This driver can also be built as a module. If so, the module will be called i2c-nforce2. +config I2C_OCORES + tristate "OpenCores I2C Controller" + depends on I2C && EXPERIMENTAL + help + If you say yes to this option, support will be included for the + OpenCores I2C controller. For details see + http://www.opencores.org/projects.cgi/web/i2c/overview + + This driver can also be built as a module. If so, the module + will be called i2c-ocores. + config I2C_PARPORT tristate "Parallel port adapter" depends on I2C && PARPORT @@ -500,6 +514,7 @@ config I2C_PCA_ISA tristate "PCA9564 on an ISA bus" depends on I2C select I2C_ALGOPCA + default n help This driver supports ISA boards using the Philips PCA 9564 Parallel bus to I2C bus controller @@ -507,6 +522,11 @@ config I2C_PCA_ISA This driver can also be built as a module. If so, the module will be called i2c-pca-isa. + This device is almost undetectable and using this driver on a + system which doesn't have this device will result in long + delays when I2C/SMBus chip drivers are loaded (e.g. at boot + time). If unsure, say N. + config I2C_MV64XXX tristate "Marvell mv64xxx I2C Controller" depends on I2C && MV64X60 && EXPERIMENTAL diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index b44831dff68..ac56df53155 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o obj-$(CONFIG_I2C_MPC) += i2c-mpc.o obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o +obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index dfca7493362..3e0d04d5a80 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1,5 +1,5 @@ /* - i801.c - Part of lm_sensors, Linux kernel modules for hardware + i2c-i801.c - Part of lm_sensors, Linux kernel modules for hardware monitoring Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker @@ -36,7 +36,7 @@ This driver supports several versions of Intel's I/O Controller Hubs (ICH). For SMBus support, they are similar to the PIIX4 and are part of Intel's '810' and other chipsets. - See the doc/busses/i2c-i801 file for details. + See the file Documentation/i2c/busses/i2c-i801 for details. I2C Block Read and Process Call are not supported. */ @@ -66,9 +66,8 @@ #define SMBAUXCTL (13 + i801_smba) /* ICH4 only */ /* PCI Address Constants */ -#define SMBBA 0x020 +#define SMBBAR 4 #define SMBHSTCFG 0x040 -#define SMBREV 0x008 /* Host configuration bits for SMBHSTCFG */ #define SMBHSTCFG_HST_EN 1 @@ -92,92 +91,16 @@ #define I801_START 0x40 #define I801_PEC_EN 0x80 /* ICH4 only */ -/* insmod parameters */ - -/* If force_addr is set to anything different from 0, we forcibly enable - the I801 at the given address. VERY DANGEROUS! */ -static u16 force_addr; -module_param(force_addr, ushort, 0); -MODULE_PARM_DESC(force_addr, - "Forcibly enable the I801 at the given address. " - "EXTREMELY DANGEROUS!"); static int i801_transaction(void); static int i801_block_transaction(union i2c_smbus_data *data, char read_write, int command, int hwpec); -static unsigned short i801_smba; +static unsigned long i801_smba; static struct pci_driver i801_driver; static struct pci_dev *I801_dev; static int isich4; -static int i801_setup(struct pci_dev *dev) -{ - int error_return = 0; - unsigned char temp; - - /* Note: we keep on searching until we have found 'function 3' */ - if(PCI_FUNC(dev->devfn) != 3) - return -ENODEV; - - I801_dev = dev; - if ((dev->device == PCI_DEVICE_ID_INTEL_82801DB_3) || - (dev->device == PCI_DEVICE_ID_INTEL_82801EB_3) || - (dev->device == PCI_DEVICE_ID_INTEL_ESB_4)) - isich4 = 1; - else - isich4 = 0; - - /* Determine the address of the SMBus areas */ - if (force_addr) { - i801_smba = force_addr & 0xfff0; - } else { - pci_read_config_word(I801_dev, SMBBA, &i801_smba); - i801_smba &= 0xfff0; - if(i801_smba == 0) { - dev_err(&dev->dev, "SMB base address uninitialized " - "- upgrade BIOS or use force_addr=0xaddr\n"); - return -ENODEV; - } - } - - if (!request_region(i801_smba, (isich4 ? 16 : 8), i801_driver.name)) { - dev_err(&dev->dev, "I801_smb region 0x%x already in use!\n", - i801_smba); - error_return = -EBUSY; - goto END; - } - - pci_read_config_byte(I801_dev, SMBHSTCFG, &temp); - temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */ - pci_write_config_byte(I801_dev, SMBHSTCFG, temp); - - /* If force_addr is set, we program the new address here. Just to make - sure, we disable the device first. */ - if (force_addr) { - pci_write_config_byte(I801_dev, SMBHSTCFG, temp & 0xfe); - pci_write_config_word(I801_dev, SMBBA, i801_smba); - pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 0x01); - dev_warn(&dev->dev, "WARNING: I801 SMBus interface set to " - "new address %04x!\n", i801_smba); - } else if ((temp & 1) == 0) { - pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 1); - dev_warn(&dev->dev, "enabling SMBus device\n"); - } - - if (temp & 0x02) - dev_dbg(&dev->dev, "I801 using Interrupt SMI# for SMBus.\n"); - else - dev_dbg(&dev->dev, "I801 using PCI Interrupt for SMBus.\n"); - - pci_read_config_byte(I801_dev, SMBREV, &temp); - dev_dbg(&dev->dev, "SMBREV = 0x%X\n", temp); - dev_dbg(&dev->dev, "I801_smba = 0x%X\n", i801_smba); - -END: - return error_return; -} - static int i801_transaction(void) { int temp; @@ -334,8 +257,8 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, /* We will always wait for a fraction of a second! */ timeout = 0; do { - temp = inb_p(SMBHSTSTS); msleep(1); + temp = inb_p(SMBHSTSTS); } while ((!(temp & 0x80)) && (timeout++ < MAX_TIMEOUT)); @@ -393,8 +316,8 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, /* wait for INTR bit as advised by Intel */ timeout = 0; do { - temp = inb_p(SMBHSTSTS); msleep(1); + temp = inb_p(SMBHSTSTS); } while ((!(temp & 0x02)) && (timeout++ < MAX_TIMEOUT)); @@ -541,25 +464,76 @@ MODULE_DEVICE_TABLE (pci, i801_ids); static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id) { + unsigned char temp; + int err; + + I801_dev = dev; + if ((dev->device == PCI_DEVICE_ID_INTEL_82801DB_3) || + (dev->device == PCI_DEVICE_ID_INTEL_82801EB_3) || + (dev->device == PCI_DEVICE_ID_INTEL_ESB_4)) + isich4 = 1; + else + isich4 = 0; + + err = pci_enable_device(dev); + if (err) { + dev_err(&dev->dev, "Failed to enable SMBus PCI device (%d)\n", + err); + goto exit; + } + + /* Determine the address of the SMBus area */ + i801_smba = pci_resource_start(dev, SMBBAR); + if (!i801_smba) { + dev_err(&dev->dev, "SMBus base address uninitialized, " + "upgrade BIOS\n"); + err = -ENODEV; + goto exit_disable; + } + + err = pci_request_region(dev, SMBBAR, i801_driver.name); + if (err) { + dev_err(&dev->dev, "Failed to request SMBus region " + "0x%lx-0x%lx\n", i801_smba, + pci_resource_end(dev, SMBBAR)); + goto exit_disable; + } - if (i801_setup(dev)) { - dev_warn(&dev->dev, - "I801 not detected, module not inserted.\n"); - return -ENODEV; + pci_read_config_byte(I801_dev, SMBHSTCFG, &temp); + temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */ + if (!(temp & SMBHSTCFG_HST_EN)) { + dev_info(&dev->dev, "Enabling SMBus device\n"); + temp |= SMBHSTCFG_HST_EN; } + pci_write_config_byte(I801_dev, SMBHSTCFG, temp); + + if (temp & SMBHSTCFG_SMB_SMI_EN) + dev_dbg(&dev->dev, "SMBus using interrupt SMI#\n"); + else + dev_dbg(&dev->dev, "SMBus using PCI Interrupt\n"); /* set up the driverfs linkage to our parent device */ i801_adapter.dev.parent = &dev->dev; snprintf(i801_adapter.name, I2C_NAME_SIZE, - "SMBus I801 adapter at %04x", i801_smba); - return i2c_add_adapter(&i801_adapter); + "SMBus I801 adapter at %04lx", i801_smba); + err = i2c_add_adapter(&i801_adapter); + if (err) { + dev_err(&dev->dev, "Failed to add SMBus adapter\n"); + goto exit_disable; + } + +exit_disable: + pci_disable_device(dev); +exit: + return err; } static void __devexit i801_remove(struct pci_dev *dev) { i2c_del_adapter(&i801_adapter); - release_region(i801_smba, (isich4 ? 16 : 8)); + pci_release_region(dev, SMBBAR); + pci_disable_device(dev); } static struct pci_driver i801_driver = { diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index 2d80eb26f68..604b49e22df 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -31,6 +31,8 @@ nForce3 250Gb MCP 00E4 nForce4 MCP 0052 nForce4 MCP-04 0034 + nForce4 MCP51 0264 + nForce4 MCP55 0368 This driver supports the 2 SMBuses that are included in the MCP of the nForce2/3/4 chipsets. @@ -64,6 +66,7 @@ struct nforce2_smbus { /* * nVidia nForce2 SMBus control register definitions + * (Newer incarnations use standard BARs 4 and 5 instead) */ #define NFORCE_PCI_SMB1 0x50 #define NFORCE_PCI_SMB2 0x54 @@ -259,6 +262,8 @@ static struct pci_device_id nforce2_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS) }, { 0 } }; @@ -266,19 +271,29 @@ static struct pci_device_id nforce2_ids[] = { MODULE_DEVICE_TABLE (pci, nforce2_ids); -static int __devinit nforce2_probe_smb (struct pci_dev *dev, int reg, - struct nforce2_smbus *smbus, char *name) +static int __devinit nforce2_probe_smb (struct pci_dev *dev, int bar, + int alt_reg, struct nforce2_smbus *smbus, const char *name) { - u16 iobase; int error; - if (pci_read_config_word(dev, reg, &iobase) != PCIBIOS_SUCCESSFUL) { - dev_err(&smbus->adapter.dev, "Error reading PCI config for %s\n", name); - return -1; + smbus->base = pci_resource_start(dev, bar); + if (smbus->base) { + smbus->size = pci_resource_len(dev, bar); + } else { + /* Older incarnations of the device used non-standard BARs */ + u16 iobase; + + if (pci_read_config_word(dev, alt_reg, &iobase) + != PCIBIOS_SUCCESSFUL) { + dev_err(&dev->dev, "Error reading PCI config for %s\n", + name); + return -1; + } + + smbus->base = iobase & PCI_BASE_ADDRESS_IO_MASK; + smbus->size = 8; } - smbus->dev = dev; - smbus->base = iobase & 0xfffc; - smbus->size = 8; + smbus->dev = dev; if (!request_region(smbus->base, smbus->size, nforce2_driver.name)) { dev_err(&smbus->adapter.dev, "Error requesting region %02x .. %02X for %s\n", @@ -313,12 +328,13 @@ static int __devinit nforce2_probe(struct pci_dev *dev, const struct pci_device_ pci_set_drvdata(dev, smbuses); /* SMBus adapter 1 */ - res1 = nforce2_probe_smb (dev, NFORCE_PCI_SMB1, &smbuses[0], "SMB1"); + res1 = nforce2_probe_smb(dev, 4, NFORCE_PCI_SMB1, &smbuses[0], "SMB1"); if (res1 < 0) { dev_err(&dev->dev, "Error probing SMB1.\n"); smbuses[0].base = 0; /* to have a check value */ } - res2 = nforce2_probe_smb (dev, NFORCE_PCI_SMB2, &smbuses[1], "SMB2"); + /* SMBus adapter 2 */ + res2 = nforce2_probe_smb(dev, 5, NFORCE_PCI_SMB2, &smbuses[1], "SMB2"); if (res2 < 0) { dev_err(&dev->dev, "Error probing SMB2.\n"); smbuses[1].base = 0; /* to have a check value */ diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c new file mode 100644 index 00000000000..592824087c4 --- /dev/null +++ b/drivers/i2c/busses/i2c-ocores.c @@ -0,0 +1,341 @@ +/* + * i2c-ocores.c: I2C bus driver for OpenCores I2C controller + * (http://www.opencores.org/projects.cgi/web/i2c/overview). + * + * Peter Korsgaard <jacmet@sunsite.dk> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/i2c-ocores.h> +#include <asm/io.h> + +struct ocores_i2c { + void __iomem *base; + int regstep; + wait_queue_head_t wait; + struct i2c_adapter adap; + struct i2c_msg *msg; + int pos; + int nmsgs; + int state; /* see STATE_ */ +}; + +/* registers */ +#define OCI2C_PRELOW 0 +#define OCI2C_PREHIGH 1 +#define OCI2C_CONTROL 2 +#define OCI2C_DATA 3 +#define OCI2C_CMD 4 /* write only */ +#define OCI2C_STATUS 4 /* read only, same address as OCI2C_CMD */ + +#define OCI2C_CTRL_IEN 0x40 +#define OCI2C_CTRL_EN 0x80 + +#define OCI2C_CMD_START 0x91 +#define OCI2C_CMD_STOP 0x41 +#define OCI2C_CMD_READ 0x21 +#define OCI2C_CMD_WRITE 0x11 +#define OCI2C_CMD_READ_ACK 0x21 +#define OCI2C_CMD_READ_NACK 0x29 +#define OCI2C_CMD_IACK 0x01 + +#define OCI2C_STAT_IF 0x01 +#define OCI2C_STAT_TIP 0x02 +#define OCI2C_STAT_ARBLOST 0x20 +#define OCI2C_STAT_BUSY 0x40 +#define OCI2C_STAT_NACK 0x80 + +#define STATE_DONE 0 +#define STATE_START 1 +#define STATE_WRITE 2 +#define STATE_READ 3 +#define STATE_ERROR 4 + +static inline void oc_setreg(struct ocores_i2c *i2c, int reg, u8 value) +{ + iowrite8(value, i2c->base + reg * i2c->regstep); +} + +static inline u8 oc_getreg(struct ocores_i2c *i2c, int reg) +{ + return ioread8(i2c->base + reg * i2c->regstep); +} + +static void ocores_process(struct ocores_i2c *i2c) +{ + struct i2c_msg *msg = i2c->msg; + u8 stat = oc_getreg(i2c, OCI2C_STATUS); + + if ((i2c->state == STATE_DONE) || (i2c->state == STATE_ERROR)) { + /* stop has been sent */ + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); + wake_up(&i2c->wait); + return; + } + + /* error? */ + if (stat & OCI2C_STAT_ARBLOST) { + i2c->state = STATE_ERROR; + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + return; + } + + if ((i2c->state == STATE_START) || (i2c->state == STATE_WRITE)) { + i2c->state = + (msg->flags & I2C_M_RD) ? STATE_READ : STATE_WRITE; + + if (stat & OCI2C_STAT_NACK) { + i2c->state = STATE_ERROR; + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + return; + } + } else + msg->buf[i2c->pos++] = oc_getreg(i2c, OCI2C_DATA); + + /* end of msg? */ + if (i2c->pos == msg->len) { + i2c->nmsgs--; + i2c->msg++; + i2c->pos = 0; + msg = i2c->msg; + + if (i2c->nmsgs) { /* end? */ + /* send start? */ + if (!(msg->flags & I2C_M_NOSTART)) { + u8 addr = (msg->addr << 1); + + if (msg->flags & I2C_M_RD) + addr |= 1; + + i2c->state = STATE_START; + + oc_setreg(i2c, OCI2C_DATA, addr); + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); + return; + } else + i2c->state = (msg->flags & I2C_M_RD) + ? STATE_READ : STATE_WRITE; + } else { + i2c->state = STATE_DONE; + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP); + return; + } + } + + if (i2c->state == STATE_READ) { + oc_setreg(i2c, OCI2C_CMD, i2c->pos == (msg->len-1) ? + OCI2C_CMD_READ_NACK : OCI2C_CMD_READ_ACK); + } else { + oc_setreg(i2c, OCI2C_DATA, msg->buf[i2c->pos++]); + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_WRITE); + } +} + +static irqreturn_t ocores_isr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ocores_i2c *i2c = dev_id; + + ocores_process(i2c); + + return IRQ_HANDLED; +} + +static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct ocores_i2c *i2c = i2c_get_adapdata(adap); + + i2c->msg = msgs; + i2c->pos = 0; + i2c->nmsgs = num; + i2c->state = STATE_START; + + oc_setreg(i2c, OCI2C_DATA, + (i2c->msg->addr << 1) | + ((i2c->msg->flags & I2C_M_RD) ? 1:0)); + + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START); + + if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) || + (i2c->state == STATE_DONE), HZ)) + return (i2c->state == STATE_DONE) ? num : -EIO; + else + return -ETIMEDOUT; +} + +static void ocores_init(struct ocores_i2c *i2c, + struct ocores_i2c_platform_data *pdata) +{ + int prescale; + u8 ctrl = oc_getreg(i2c, OCI2C_CONTROL); + + /* make sure the device is disabled */ + oc_setreg(i2c, OCI2C_CONTROL, ctrl & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN)); + + prescale = (pdata->clock_khz / (5*100)) - 1; + oc_setreg(i2c, OCI2C_PRELOW, prescale & 0xff); + oc_setreg(i2c, OCI2C_PREHIGH, prescale >> 8); + + /* Init the device */ + oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_IACK); + oc_setreg(i2c, OCI2C_CONTROL, ctrl | OCI2C_CTRL_IEN | OCI2C_CTRL_EN); +} + + +static u32 ocores_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm ocores_algorithm = { + .master_xfer = ocores_xfer, + .functionality = ocores_func, +}; + +static struct i2c_adapter ocores_adapter = { + .owner = THIS_MODULE, + .name = "i2c-ocores", + .class = I2C_CLASS_HWMON, + .algo = &ocores_algorithm, +}; + + +static int __devinit ocores_i2c_probe(struct platform_device *pdev) +{ + struct ocores_i2c *i2c; + struct ocores_i2c_platform_data *pdata; + struct resource *res, *res2; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res2) + return -ENODEV; + + pdata = (struct ocores_i2c_platform_data*) pdev->dev.platform_data; + if (!pdata) + return -ENODEV; + + i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + if (!request_mem_region(res->start, res->end - res->start + 1, + pdev->name)) { + dev_err(&pdev->dev, "Memory region busy\n"); + ret = -EBUSY; + goto request_mem_failed; + } + + i2c->base = ioremap(res->start, res->end - res->start + 1); + if (!i2c->base) { + dev_err(&pdev->dev, "Unable to map registers\n"); + ret = -EIO; + goto map_failed; + } + + i2c->regstep = pdata->regstep; + ocores_init(i2c, pdata); + + init_waitqueue_head(&i2c->wait); + ret = request_irq(res2->start, ocores_isr, 0, pdev->name, i2c); + if (ret) { + dev_err(&pdev->dev, "Cannot claim IRQ\n"); + goto request_irq_failed; + } + + /* hook up driver to tree */ + platform_set_drvdata(pdev, i2c); + i2c->adap = ocores_adapter; + i2c_set_adapdata(&i2c->adap, i2c); + i2c->adap.dev.parent = &pdev->dev; + + /* add i2c adapter to i2c tree */ + ret = i2c_add_adapter(&i2c->adap); + if (ret) { + dev_err(&pdev->dev, "Failed to add adapter\n"); + goto add_adapter_failed; + } + + return 0; + +add_adapter_failed: + free_irq(res2->start, i2c); +request_irq_failed: + iounmap(i2c->base); +map_failed: + release_mem_region(res->start, res->end - res->start + 1); +request_mem_failed: + kfree(i2c); + + return ret; +} + +static int __devexit ocores_i2c_remove(struct platform_device* pdev) +{ + struct ocores_i2c *i2c = platform_get_drvdata(pdev); + struct resource *res; + + /* disable i2c logic */ + oc_setreg(i2c, OCI2C_CONTROL, oc_getreg(i2c, OCI2C_CONTROL) + & ~(OCI2C_CTRL_EN|OCI2C_CTRL_IEN)); + + /* remove adapter & data */ + i2c_del_adapter(&i2c->adap); + platform_set_drvdata(pdev, NULL); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res) + free_irq(res->start, i2c); + + iounmap(i2c->base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + release_mem_region(res->start, res->end - res->start + 1); + + kfree(i2c); + + return 0; +} + +static struct platform_driver ocores_i2c_driver = { + .probe = ocores_i2c_probe, + .remove = __devexit_p(ocores_i2c_remove), + .driver = { + .owner = THIS_MODULE, + .name = "ocores-i2c", + }, +}; + +static int __init ocores_i2c_init(void) +{ + return platform_driver_register(&ocores_i2c_driver); +} + +static void __exit ocores_i2c_exit(void) +{ + platform_driver_unregister(&ocores_i2c_driver); +} + +module_init(ocores_i2c_init); +module_exit(ocores_i2c_exit); + +MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>"); +MODULE_DESCRIPTION("OpenCores I2C bus driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index d9c7c00e71f..8f2f65b793b 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -102,13 +102,6 @@ MODULE_PARM_DESC(force_addr, "Forcibly enable the PIIX4 at the given address. " "EXTREMELY DANGEROUS!"); -/* If fix_hstcfg is set to anything different from 0, we reset one of the - registers to be a valid value. */ -static int fix_hstcfg; -module_param (fix_hstcfg, int, 0); -MODULE_PARM_DESC(fix_hstcfg, - "Fix config register. Needed on some boards (Force CPCI735)."); - static int piix4_transaction(void); static unsigned short piix4_smba; @@ -137,7 +130,7 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev, /* Don't access SMBus on IBM systems which get corrupted eeproms */ if (dmi_check_system(piix4_dmi_table) && PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) { - dev_err(&PIIX4_dev->dev, "IBM Laptop detected; this module " + dev_err(&PIIX4_dev->dev, "IBM system detected; this module " "may corrupt your serial eeprom! Refusing to load " "module!\n"); return -EPERM; @@ -166,22 +159,6 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev, pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp); - /* Some BIOS will set up the chipset incorrectly and leave a register - in an undefined state (causing I2C to act very strangely). */ - if (temp & 0x02) { - if (fix_hstcfg) { - dev_info(&PIIX4_dev->dev, "Working around buggy BIOS " - "(I2C)\n"); - temp &= 0xfd; - pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp); - } else { - dev_info(&PIIX4_dev->dev, "Unusual config register " - "value\n"); - dev_info(&PIIX4_dev->dev, "Try using fix_hstcfg=1 if " - "you experience problems\n"); - } - } - /* If force_addr is set, we program the new address here. Just to make sure, we disable the PIIX4 first. */ if (force_addr) { @@ -214,7 +191,7 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev, } } - if ((temp & 0x0E) == 8) + if (((temp & 0x0E) == 8) || ((temp & 0x0E) == 2)) dev_dbg(&PIIX4_dev->dev, "Using Interrupt 9 for SMBus.\n"); else if ((temp & 0x0E) == 0) dev_dbg(&PIIX4_dev->dev, "Using Interrupt SMI# for SMBus.\n"); @@ -413,6 +390,12 @@ static struct i2c_adapter piix4_adapter = { static struct pci_device_id piix4_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3), .driver_data = 3 }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP200_SMBUS), + .driver_data = 0 }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS), + .driver_data = 0 }, + { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS), + .driver_data = 0 }, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4), .driver_data = 0 }, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5), diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c index 766cc969c4d..22a3eda0416 100644 --- a/drivers/i2c/busses/scx200_acb.c +++ b/drivers/i2c/busses/scx200_acb.c @@ -33,7 +33,6 @@ #include <linux/delay.h> #include <linux/mutex.h> #include <asm/io.h> -#include <asm/msr.h> #include <linux/scx200.h> @@ -85,6 +84,10 @@ struct scx200_acb_iface { u8 *ptr; char needs_reset; unsigned len; + + /* PCI device info */ + struct pci_dev *pdev; + int bar; }; /* Register Definitions */ @@ -381,7 +384,7 @@ static struct i2c_algorithm scx200_acb_algorithm = { static struct scx200_acb_iface *scx200_acb_list; static DECLARE_MUTEX(scx200_acb_list_mutex); -static int scx200_acb_probe(struct scx200_acb_iface *iface) +static __init int scx200_acb_probe(struct scx200_acb_iface *iface) { u8 val; @@ -417,17 +420,16 @@ static int scx200_acb_probe(struct scx200_acb_iface *iface) return 0; } -static int __init scx200_acb_create(const char *text, int base, int index) +static __init struct scx200_acb_iface *scx200_create_iface(const char *text, + int index) { struct scx200_acb_iface *iface; struct i2c_adapter *adapter; - int rc; iface = kzalloc(sizeof(*iface), GFP_KERNEL); if (!iface) { printk(KERN_ERR NAME ": can't allocate memory\n"); - rc = -ENOMEM; - goto errout; + return NULL; } adapter = &iface->adapter; @@ -440,26 +442,27 @@ static int __init scx200_acb_create(const char *text, int base, int index) mutex_init(&iface->mutex); - if (!request_region(base, 8, adapter->name)) { - printk(KERN_ERR NAME ": can't allocate io 0x%x-0x%x\n", - base, base + 8-1); - rc = -EBUSY; - goto errout_free; - } - iface->base = base; + return iface; +} + +static int __init scx200_acb_create(struct scx200_acb_iface *iface) +{ + struct i2c_adapter *adapter; + int rc; + + adapter = &iface->adapter; rc = scx200_acb_probe(iface); if (rc) { printk(KERN_WARNING NAME ": probe failed\n"); - goto errout_release; + return rc; } scx200_acb_reset(iface); if (i2c_add_adapter(adapter) < 0) { printk(KERN_ERR NAME ": failed to register\n"); - rc = -ENODEV; - goto errout_release; + return -ENODEV; } down(&scx200_acb_list_mutex); @@ -468,64 +471,148 @@ static int __init scx200_acb_create(const char *text, int base, int index) up(&scx200_acb_list_mutex); return 0; +} - errout_release: - release_region(iface->base, 8); +static __init int scx200_create_pci(const char *text, struct pci_dev *pdev, + int bar) +{ + struct scx200_acb_iface *iface; + int rc; + + iface = scx200_create_iface(text, 0); + + if (iface == NULL) + return -ENOMEM; + + iface->pdev = pdev; + iface->bar = bar; + + pci_enable_device_bars(iface->pdev, 1 << iface->bar); + + rc = pci_request_region(iface->pdev, iface->bar, iface->adapter.name); + + if (rc != 0) { + printk(KERN_ERR NAME ": can't allocate PCI BAR %d\n", + iface->bar); + goto errout_free; + } + + iface->base = pci_resource_start(iface->pdev, iface->bar); + rc = scx200_acb_create(iface); + + if (rc == 0) + return 0; + + pci_release_region(iface->pdev, iface->bar); + pci_dev_put(iface->pdev); errout_free: kfree(iface); - errout: return rc; } -static struct pci_device_id scx200[] = { - { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE) }, - { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE) }, - { }, -}; +static int __init scx200_create_isa(const char *text, unsigned long base, + int index) +{ + struct scx200_acb_iface *iface; + int rc; + + iface = scx200_create_iface(text, index); + + if (iface == NULL) + return -ENOMEM; + + if (request_region(base, 8, iface->adapter.name) == 0) { + printk(KERN_ERR NAME ": can't allocate io 0x%lx-0x%lx\n", + base, base + 8 - 1); + rc = -EBUSY; + goto errout_free; + } + + iface->base = base; + rc = scx200_acb_create(iface); + + if (rc == 0) + return 0; -static struct pci_device_id divil_pci[] = { - { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) }, - { } /* NULL entry */ + release_region(base, 8); + errout_free: + kfree(iface); + return rc; +} + +/* Driver data is an index into the scx200_data array that indicates + * the name and the BAR where the I/O address resource is located. ISA + * devices are flagged with a bar value of -1 */ + +static struct pci_device_id scx200_pci[] = { + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE), + .driver_data = 0 }, + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE), + .driver_data = 0 }, + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA), + .driver_data = 1 }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA), + .driver_data = 2 } }; -#define MSR_LBAR_SMB 0x5140000B +static struct { + const char *name; + int bar; +} scx200_data[] = { + { "SCx200", -1 }, + { "CS5535", 0 }, + { "CS5536", 0 } +}; -static __init int scx200_add_cs553x(void) +static __init int scx200_scan_pci(void) { - u32 low, hi; - u32 smb_base; - - /* Grab & reserve the SMB I/O range */ - rdmsr(MSR_LBAR_SMB, low, hi); + int data, dev; + int rc = -ENODEV; + struct pci_dev *pdev; + + for(dev = 0; dev < ARRAY_SIZE(scx200_pci); dev++) { + pdev = pci_get_device(scx200_pci[dev].vendor, + scx200_pci[dev].device, NULL); + + if (pdev == NULL) + continue; + + data = scx200_pci[dev].driver_data; + + /* if .bar is greater or equal to zero, this is a + * PCI device - otherwise, we assume + that the ports are ISA based + */ + + if (scx200_data[data].bar >= 0) + rc = scx200_create_pci(scx200_data[data].name, pdev, + scx200_data[data].bar); + else { + int i; + + for (i = 0; i < MAX_DEVICES; ++i) { + if (base[i] == 0) + continue; + + rc = scx200_create_isa(scx200_data[data].name, + base[i], + i); + } + } - /* Check the IO mask and whether SMB is enabled */ - if (hi != 0x0000F001) { - printk(KERN_WARNING NAME ": SMBus not enabled\n"); - return -ENODEV; + break; } - /* SMBus IO size is 8 bytes */ - smb_base = low & 0x0000FFF8; - - return scx200_acb_create("CS5535", smb_base, 0); + return rc; } static int __init scx200_acb_init(void) { - int i; - int rc = -ENODEV; + int rc; pr_debug(NAME ": NatSemi SCx200 ACCESS.bus Driver\n"); - /* Verify that this really is a SCx200 processor */ - if (pci_dev_present(scx200)) { - for (i = 0; i < MAX_DEVICES; ++i) { - if (base[i] > 0) - rc = scx200_acb_create("SCx200", base[i], i); - } - } else if (pci_dev_present(divil_pci)) - rc = scx200_add_cs553x(); + rc = scx200_scan_pci(); /* If at least one bus was created, init must succeed */ if (scx200_acb_list) @@ -543,7 +630,14 @@ static void __exit scx200_acb_cleanup(void) up(&scx200_acb_list_mutex); i2c_del_adapter(&iface->adapter); - release_region(iface->base, 8); + + if (iface->pdev) { + pci_release_region(iface->pdev, iface->bar); + pci_dev_put(iface->pdev); + } + else + release_region(iface->base, 8); + kfree(iface); down(&scx200_acb_list_mutex); } diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 7aa5c38f085..87ee3ce5861 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -39,6 +39,7 @@ config SENSORS_EEPROM config SENSORS_PCF8574 tristate "Philips PCF8574 and PCF8574A" depends on I2C && EXPERIMENTAL + default n help If you say yes here you get support for Philips PCF8574 and PCF8574A chips. @@ -46,6 +47,9 @@ config SENSORS_PCF8574 This driver can also be built as a module. If so, the module will be called pcf8574. + These devices are hard to detect and rarely found on mainstream + hardware. If unsure, say N. + config SENSORS_PCA9539 tristate "Philips PCA9539 16-bit I/O port" depends on I2C && EXPERIMENTAL @@ -59,12 +63,16 @@ config SENSORS_PCA9539 config SENSORS_PCF8591 tristate "Philips PCF8591" depends on I2C && EXPERIMENTAL + default n help If you say yes here you get support for Philips PCF8591 chips. This driver can also be built as a module. If so, the module will be called pcf8591. + These devices are hard to detect and rarely found on mainstream + hardware. If unsure, say N. + config ISP1301_OMAP tristate "Philips ISP1301 with OMAP OTG" depends on I2C && ARCH_OMAP_OTG diff --git a/drivers/i2c/chips/m41t00.c b/drivers/i2c/chips/m41t00.c index 99ab4ec3439..2dd0a34d947 100644 --- a/drivers/i2c/chips/m41t00.c +++ b/drivers/i2c/chips/m41t00.c @@ -1,11 +1,9 @@ /* - * drivers/i2c/chips/m41t00.c - * - * I2C client/driver for the ST M41T00 Real-Time Clock chip. + * I2C client/driver for the ST M41T00 family of i2c rtc chips. * * Author: Mark A. Greer <mgreer@mvista.com> * - * 2005 (c) MontaVista Software, Inc. This file is licensed under + * 2005, 2006 (c) MontaVista Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. @@ -13,9 +11,6 @@ /* * This i2c client/driver wedges between the drivers/char/genrtc.c RTC * interface and the SMBus interface of the i2c subsystem. - * It would be more efficient to use i2c msgs/i2c_transfer directly but, as - * recommened in .../Documentation/i2c/writing-clients section - * "Sending and receiving", using SMBus level communication is preferred. */ #include <linux/kernel.h> @@ -24,56 +19,110 @@ #include <linux/i2c.h> #include <linux/rtc.h> #include <linux/bcd.h> -#include <linux/mutex.h> #include <linux/workqueue.h> - +#include <linux/platform_device.h> +#include <linux/m41t00.h> #include <asm/time.h> #include <asm/rtc.h> -#define M41T00_DRV_NAME "m41t00" - -static DEFINE_MUTEX(m41t00_mutex); - static struct i2c_driver m41t00_driver; static struct i2c_client *save_client; static unsigned short ignore[] = { I2C_CLIENT_END }; -static unsigned short normal_addr[] = { 0x68, I2C_CLIENT_END }; +static unsigned short normal_addr[] = { I2C_CLIENT_END, I2C_CLIENT_END }; static struct i2c_client_address_data addr_data = { - .normal_i2c = normal_addr, - .probe = ignore, - .ignore = ignore, + .normal_i2c = normal_addr, + .probe = ignore, + .ignore = ignore, +}; + +struct m41t00_chip_info { + u8 type; + char *name; + u8 read_limit; + u8 sec; /* Offsets for chip regs */ + u8 min; + u8 hour; + u8 day; + u8 mon; + u8 year; + u8 alarm_mon; + u8 alarm_hour; + u8 sqw; + u8 sqw_freq; }; +static struct m41t00_chip_info m41t00_chip_info_tbl[] = { + { + .type = M41T00_TYPE_M41T00, + .name = "m41t00", + .read_limit = 5, + .sec = 0, + .min = 1, + .hour = 2, + .day = 4, + .mon = 5, + .year = 6, + }, + { + .type = M41T00_TYPE_M41T81, + .name = "m41t81", + .read_limit = 1, + .sec = 1, + .min = 2, + .hour = 3, + .day = 5, + .mon = 6, + .year = 7, + .alarm_mon = 0xa, + .alarm_hour = 0xc, + .sqw = 0x13, + }, + { + .type = M41T00_TYPE_M41T85, + .name = "m41t85", + .read_limit = 1, + .sec = 1, + .min = 2, + .hour = 3, + .day = 5, + .mon = 6, + .year = 7, + .alarm_mon = 0xa, + .alarm_hour = 0xc, + .sqw = 0x13, + }, +}; +static struct m41t00_chip_info *m41t00_chip; + ulong m41t00_get_rtc_time(void) { - s32 sec, min, hour, day, mon, year; - s32 sec1, min1, hour1, day1, mon1, year1; - ulong limit = 10; + s32 sec, min, hour, day, mon, year; + s32 sec1, min1, hour1, day1, mon1, year1; + u8 reads = 0; + u8 buf[8], msgbuf[1] = { 0 }; /* offset into rtc's regs */ + struct i2c_msg msgs[] = { + { + .addr = save_client->addr, + .flags = 0, + .len = 1, + .buf = msgbuf, + }, + { + .addr = save_client->addr, + .flags = I2C_M_RD, + .len = 8, + .buf = buf, + }, + }; sec = min = hour = day = mon = year = 0; - sec1 = min1 = hour1 = day1 = mon1 = year1 = 0; - mutex_lock(&m41t00_mutex); do { - if (((sec = i2c_smbus_read_byte_data(save_client, 0)) >= 0) - && ((min = i2c_smbus_read_byte_data(save_client, 1)) - >= 0) - && ((hour = i2c_smbus_read_byte_data(save_client, 2)) - >= 0) - && ((day = i2c_smbus_read_byte_data(save_client, 4)) - >= 0) - && ((mon = i2c_smbus_read_byte_data(save_client, 5)) - >= 0) - && ((year = i2c_smbus_read_byte_data(save_client, 6)) - >= 0) - && ((sec == sec1) && (min == min1) && (hour == hour1) - && (day == day1) && (mon == mon1) - && (year == year1))) - - break; + if (i2c_transfer(save_client->adapter, msgs, 2) < 0) + goto read_err; sec1 = sec; min1 = min; @@ -81,69 +130,88 @@ m41t00_get_rtc_time(void) day1 = day; mon1 = mon; year1 = year; - } while (--limit > 0); - mutex_unlock(&m41t00_mutex); - - if (limit == 0) { - dev_warn(&save_client->dev, - "m41t00: can't read rtc chip\n"); - sec = min = hour = day = mon = year = 0; - } - - sec &= 0x7f; - min &= 0x7f; - hour &= 0x3f; - day &= 0x3f; - mon &= 0x1f; - year &= 0xff; - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); + sec = buf[m41t00_chip->sec] & 0x7f; + min = buf[m41t00_chip->min] & 0x7f; + hour = buf[m41t00_chip->hour] & 0x3f; + day = buf[m41t00_chip->day] & 0x3f; + mon = buf[m41t00_chip->mon] & 0x1f; + year = buf[m41t00_chip->year]; + } while ((++reads < m41t00_chip->read_limit) && ((sec != sec1) + || (min != min1) || (hour != hour1) || (day != day1) + || (mon != mon1) || (year != year1))); + + if ((m41t00_chip->read_limit > 1) && ((sec != sec1) || (min != min1) + || (hour != hour1) || (day != day1) || (mon != mon1) + || (year != year1))) + goto read_err; + + sec = BCD2BIN(sec); + min = BCD2BIN(min); + hour = BCD2BIN(hour); + day = BCD2BIN(day); + mon = BCD2BIN(mon); + year = BCD2BIN(year); year += 1900; if (year < 1970) year += 100; return mktime(year, mon, day, hour, min, sec); + +read_err: + dev_err(&save_client->dev, "m41t00_get_rtc_time: Read error\n"); + return 0; } +EXPORT_SYMBOL_GPL(m41t00_get_rtc_time); static void m41t00_set(void *arg) { struct rtc_time tm; - ulong nowtime = *(ulong *)arg; + int nowtime = *(int *)arg; + s32 sec, min, hour, day, mon, year; + u8 wbuf[9], *buf = &wbuf[1], msgbuf[1] = { 0 }; + struct i2c_msg msgs[] = { + { + .addr = save_client->addr, + .flags = 0, + .len = 1, + .buf = msgbuf, + }, + { + .addr = save_client->addr, + .flags = I2C_M_RD, + .len = 8, + .buf = buf, + }, + }; to_tm(nowtime, &tm); tm.tm_year = (tm.tm_year - 1900) % 100; - BIN_TO_BCD(tm.tm_sec); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_mon); - BIN_TO_BCD(tm.tm_mday); - BIN_TO_BCD(tm.tm_year); - - mutex_lock(&m41t00_mutex); - if ((i2c_smbus_write_byte_data(save_client, 0, tm.tm_sec & 0x7f) < 0) - || (i2c_smbus_write_byte_data(save_client, 1, tm.tm_min & 0x7f) - < 0) - || (i2c_smbus_write_byte_data(save_client, 2, tm.tm_hour & 0x3f) - < 0) - || (i2c_smbus_write_byte_data(save_client, 4, tm.tm_mday & 0x3f) - < 0) - || (i2c_smbus_write_byte_data(save_client, 5, tm.tm_mon & 0x1f) - < 0) - || (i2c_smbus_write_byte_data(save_client, 6, tm.tm_year & 0xff) - < 0)) - - dev_warn(&save_client->dev,"m41t00: can't write to rtc chip\n"); - - mutex_unlock(&m41t00_mutex); - return; + sec = BIN2BCD(tm.tm_sec); + min = BIN2BCD(tm.tm_min); + hour = BIN2BCD(tm.tm_hour); + day = BIN2BCD(tm.tm_mday); + mon = BIN2BCD(tm.tm_mon); + year = BIN2BCD(tm.tm_year); + + /* Read reg values into buf[0..7]/wbuf[1..8] */ + if (i2c_transfer(save_client->adapter, msgs, 2) < 0) { + dev_err(&save_client->dev, "m41t00_set: Read error\n"); + return; + } + + wbuf[0] = 0; /* offset into rtc's regs */ + buf[m41t00_chip->sec] = (buf[m41t00_chip->sec] & ~0x7f) | (sec & 0x7f); + buf[m41t00_chip->min] = (buf[m41t00_chip->min] & ~0x7f) | (min & 0x7f); + buf[m41t00_chip->hour] = (buf[m41t00_chip->hour] & ~0x3f) | (hour& 0x3f); + buf[m41t00_chip->day] = (buf[m41t00_chip->day] & ~0x3f) | (day & 0x3f); + buf[m41t00_chip->mon] = (buf[m41t00_chip->mon] & ~0x1f) | (mon & 0x1f); + + if (i2c_master_send(save_client, wbuf, 9) < 0) + dev_err(&save_client->dev, "m41t00_set: Write error\n"); } static ulong new_time; @@ -162,6 +230,48 @@ m41t00_set_rtc_time(ulong nowtime) return 0; } +EXPORT_SYMBOL_GPL(m41t00_set_rtc_time); + +/* + ***************************************************************************** + * + * platform_data Driver Interface + * + ***************************************************************************** + */ +static int __init +m41t00_platform_probe(struct platform_device *pdev) +{ + struct m41t00_platform_data *pdata; + int i; + + if (pdev && (pdata = pdev->dev.platform_data)) { + normal_addr[0] = pdata->i2c_addr; + + for (i=0; i<ARRAY_SIZE(m41t00_chip_info_tbl); i++) + if (m41t00_chip_info_tbl[i].type == pdata->type) { + m41t00_chip = &m41t00_chip_info_tbl[i]; + m41t00_chip->sqw_freq = pdata->sqw_freq; + return 0; + } + } + return -ENODEV; +} + +static int __exit +m41t00_platform_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver m41t00_platform_driver = { + .probe = m41t00_platform_probe, + .remove = m41t00_platform_remove, + .driver = { + .owner = THIS_MODULE, + .name = M41T00_DRV_NAME, + }, +}; /* ***************************************************************************** @@ -176,23 +286,71 @@ m41t00_probe(struct i2c_adapter *adap, int addr, int kind) struct i2c_client *client; int rc; + if (!i2c_check_functionality(adap, I2C_FUNC_I2C + | I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (!client) return -ENOMEM; - strncpy(client->name, M41T00_DRV_NAME, I2C_NAME_SIZE); + strlcpy(client->name, m41t00_chip->name, I2C_NAME_SIZE); client->addr = addr; client->adapter = adap; client->driver = &m41t00_driver; - if ((rc = i2c_attach_client(client)) != 0) { - kfree(client); - return rc; + if ((rc = i2c_attach_client(client))) + goto attach_err; + + if (m41t00_chip->type != M41T00_TYPE_M41T00) { + /* If asked, disable SQW, set SQW frequency & re-enable */ + if (m41t00_chip->sqw_freq) + if (((rc = i2c_smbus_read_byte_data(client, + m41t00_chip->alarm_mon)) < 0) + || ((rc = i2c_smbus_write_byte_data(client, + m41t00_chip->alarm_mon, rc & ~0x40)) <0) + || ((rc = i2c_smbus_write_byte_data(client, + m41t00_chip->sqw, + m41t00_chip->sqw_freq)) < 0) + || ((rc = i2c_smbus_write_byte_data(client, + m41t00_chip->alarm_mon, rc | 0x40)) <0)) + goto sqw_err; + + /* Make sure HT (Halt Update) bit is cleared */ + if ((rc = i2c_smbus_read_byte_data(client, + m41t00_chip->alarm_hour)) < 0) + goto ht_err; + + if (rc & 0x40) + if ((rc = i2c_smbus_write_byte_data(client, + m41t00_chip->alarm_hour, rc & ~0x40))<0) + goto ht_err; } - m41t00_wq = create_singlethread_workqueue("m41t00"); + /* Make sure ST (stop) bit is cleared */ + if ((rc = i2c_smbus_read_byte_data(client, m41t00_chip->sec)) < 0) + goto st_err; + + if (rc & 0x80) + if ((rc = i2c_smbus_write_byte_data(client, m41t00_chip->sec, + rc & ~0x80)) < 0) + goto st_err; + + m41t00_wq = create_singlethread_workqueue(m41t00_chip->name); save_client = client; return 0; + +st_err: + dev_err(&client->dev, "m41t00_probe: Can't clear ST bit\n"); + goto attach_err; +ht_err: + dev_err(&client->dev, "m41t00_probe: Can't clear HT bit\n"); + goto attach_err; +sqw_err: + dev_err(&client->dev, "m41t00_probe: Can't set SQW Frequency\n"); +attach_err: + kfree(client); + return rc; } static int @@ -204,7 +362,7 @@ m41t00_attach(struct i2c_adapter *adap) static int m41t00_detach(struct i2c_client *client) { - int rc; + int rc; if ((rc = i2c_detach_client(client)) == 0) { kfree(client); @@ -225,14 +383,18 @@ static struct i2c_driver m41t00_driver = { static int __init m41t00_init(void) { - return i2c_add_driver(&m41t00_driver); + int rc; + + if (!(rc = platform_driver_register(&m41t00_platform_driver))) + rc = i2c_add_driver(&m41t00_driver); + return rc; } static void __exit m41t00_exit(void) { i2c_del_driver(&m41t00_driver); - return; + platform_driver_unregister(&m41t00_platform_driver); } module_init(m41t00_init); diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 45e2cdf5473..a45155f799d 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -916,7 +916,7 @@ s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value) } s32 i2c_smbus_write_block_data(struct i2c_client *client, u8 command, - u8 length, u8 *values) + u8 length, const u8 *values) { union i2c_smbus_data data; @@ -944,7 +944,7 @@ s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command, u8 *val } s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client, u8 command, - u8 length, u8 *values) + u8 length, const u8 *values) { union i2c_smbus_data data; diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index ed7eed388ba..58ccddd5c23 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -426,10 +426,7 @@ static int i2cdev_attach_adapter(struct i2c_adapter *adap) /* register this i2c device with the driver core */ i2c_dev->adap = adap; - if (adap->dev.parent == &platform_bus) - dev = &adap->dev; - else - dev = adap->dev.parent; + dev = &adap->dev; i2c_dev->class_dev = class_device_create(i2c_dev_class, NULL, MKDEV(I2C_MAJOR, i2c_dev->minor), dev, "i2c-%d", i2c_dev->minor); diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 0b5ff553e39..c63d4e7984b 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -2268,7 +2268,7 @@ static int powerbook_sleep_grackle(void) _set_L2CR(save_l2cr); /* Restore userland MMU context */ - set_context(current->active_mm->context, current->active_mm->pgd); + set_context(current->active_mm->context.id, current->active_mm->pgd); /* Power things up */ pmu_unlock(); @@ -2366,7 +2366,7 @@ powerbook_sleep_Core99(void) _set_L3CR(save_l3cr); /* Restore userland MMU context */ - set_context(current->active_mm->context, current->active_mm->pgd); + set_context(current->active_mm->context.id, current->active_mm->pgd); /* Tell PMU we are ready */ pmu_unlock(); diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 46d8c01437e..a26077a175a 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -401,6 +401,11 @@ static void cp_clean_rings (struct cp_private *cp); #ifdef CONFIG_NET_POLL_CONTROLLER static void cp_poll_controller(struct net_device *dev); #endif +static int cp_get_eeprom_len(struct net_device *dev); +static int cp_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data); +static int cp_set_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data); static struct pci_device_id cp_pci_tbl[] = { { PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8139, @@ -1577,6 +1582,9 @@ static struct ethtool_ops cp_ethtool_ops = { .get_strings = cp_get_strings, .get_ethtool_stats = cp_get_ethtool_stats, .get_perm_addr = ethtool_op_get_perm_addr, + .get_eeprom_len = cp_get_eeprom_len, + .get_eeprom = cp_get_eeprom, + .set_eeprom = cp_set_eeprom, }; static int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) @@ -1612,24 +1620,32 @@ static int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) #define eeprom_delay() readl(ee_addr) /* The EEPROM commands include the alway-set leading bit. */ +#define EE_EXTEND_CMD (4) #define EE_WRITE_CMD (5) #define EE_READ_CMD (6) #define EE_ERASE_CMD (7) -static int read_eeprom (void __iomem *ioaddr, int location, int addr_len) -{ - int i; - unsigned retval = 0; - void __iomem *ee_addr = ioaddr + Cfg9346; - int read_cmd = location | (EE_READ_CMD << addr_len); +#define EE_EWDS_ADDR (0) +#define EE_WRAL_ADDR (1) +#define EE_ERAL_ADDR (2) +#define EE_EWEN_ADDR (3) + +#define CP_EEPROM_MAGIC PCI_DEVICE_ID_REALTEK_8139 +static void eeprom_cmd_start(void __iomem *ee_addr) +{ writeb (EE_ENB & ~EE_CS, ee_addr); writeb (EE_ENB, ee_addr); eeprom_delay (); +} - /* Shift the read command bits out. */ - for (i = 4 + addr_len; i >= 0; i--) { - int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; +static void eeprom_cmd(void __iomem *ee_addr, int cmd, int cmd_len) +{ + int i; + + /* Shift the command bits out. */ + for (i = cmd_len - 1; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? EE_DATA_WRITE : 0; writeb (EE_ENB | dataval, ee_addr); eeprom_delay (); writeb (EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); @@ -1637,6 +1653,33 @@ static int read_eeprom (void __iomem *ioaddr, int location, int addr_len) } writeb (EE_ENB, ee_addr); eeprom_delay (); +} + +static void eeprom_cmd_end(void __iomem *ee_addr) +{ + writeb (~EE_CS, ee_addr); + eeprom_delay (); +} + +static void eeprom_extend_cmd(void __iomem *ee_addr, int extend_cmd, + int addr_len) +{ + int cmd = (EE_EXTEND_CMD << addr_len) | (extend_cmd << (addr_len - 2)); + + eeprom_cmd_start(ee_addr); + eeprom_cmd(ee_addr, cmd, 3 + addr_len); + eeprom_cmd_end(ee_addr); +} + +static u16 read_eeprom (void __iomem *ioaddr, int location, int addr_len) +{ + int i; + u16 retval = 0; + void __iomem *ee_addr = ioaddr + Cfg9346; + int read_cmd = location | (EE_READ_CMD << addr_len); + + eeprom_cmd_start(ee_addr); + eeprom_cmd(ee_addr, read_cmd, 3 + addr_len); for (i = 16; i > 0; i--) { writeb (EE_ENB | EE_SHIFT_CLK, ee_addr); @@ -1648,13 +1691,125 @@ static int read_eeprom (void __iomem *ioaddr, int location, int addr_len) eeprom_delay (); } - /* Terminate the EEPROM access. */ - writeb (~EE_CS, ee_addr); - eeprom_delay (); + eeprom_cmd_end(ee_addr); return retval; } +static void write_eeprom(void __iomem *ioaddr, int location, u16 val, + int addr_len) +{ + int i; + void __iomem *ee_addr = ioaddr + Cfg9346; + int write_cmd = location | (EE_WRITE_CMD << addr_len); + + eeprom_extend_cmd(ee_addr, EE_EWEN_ADDR, addr_len); + + eeprom_cmd_start(ee_addr); + eeprom_cmd(ee_addr, write_cmd, 3 + addr_len); + eeprom_cmd(ee_addr, val, 16); + eeprom_cmd_end(ee_addr); + + eeprom_cmd_start(ee_addr); + for (i = 0; i < 20000; i++) + if (readb(ee_addr) & EE_DATA_READ) + break; + eeprom_cmd_end(ee_addr); + + eeprom_extend_cmd(ee_addr, EE_EWDS_ADDR, addr_len); +} + +static int cp_get_eeprom_len(struct net_device *dev) +{ + struct cp_private *cp = netdev_priv(dev); + int size; + + spin_lock_irq(&cp->lock); + size = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 256 : 128; + spin_unlock_irq(&cp->lock); + + return size; +} + +static int cp_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct cp_private *cp = netdev_priv(dev); + unsigned int addr_len; + u16 val; + u32 offset = eeprom->offset >> 1; + u32 len = eeprom->len; + u32 i = 0; + + eeprom->magic = CP_EEPROM_MAGIC; + + spin_lock_irq(&cp->lock); + + addr_len = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 8 : 6; + + if (eeprom->offset & 1) { + val = read_eeprom(cp->regs, offset, addr_len); + data[i++] = (u8)(val >> 8); + offset++; + } + + while (i < len - 1) { + val = read_eeprom(cp->regs, offset, addr_len); + data[i++] = (u8)val; + data[i++] = (u8)(val >> 8); + offset++; + } + + if (i < len) { + val = read_eeprom(cp->regs, offset, addr_len); + data[i] = (u8)val; + } + + spin_unlock_irq(&cp->lock); + return 0; +} + +static int cp_set_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct cp_private *cp = netdev_priv(dev); + unsigned int addr_len; + u16 val; + u32 offset = eeprom->offset >> 1; + u32 len = eeprom->len; + u32 i = 0; + + if (eeprom->magic != CP_EEPROM_MAGIC) + return -EINVAL; + + spin_lock_irq(&cp->lock); + + addr_len = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 8 : 6; + + if (eeprom->offset & 1) { + val = read_eeprom(cp->regs, offset, addr_len) & 0xff; + val |= (u16)data[i++] << 8; + write_eeprom(cp->regs, offset, val, addr_len); + offset++; + } + + while (i < len - 1) { + val = (u16)data[i++]; + val |= (u16)data[i++] << 8; + write_eeprom(cp->regs, offset, val, addr_len); + offset++; + } + + if (i < len) { + val = read_eeprom(cp->regs, offset, addr_len) & 0xff00; + val |= (u16)data[i]; + write_eeprom(cp->regs, offset, val, addr_len); + } + + spin_unlock_irq(&cp->lock); + return 0; +} + /* Put the board into D3cold state and wait for WakeUp signal */ static void cp_set_d3_state (struct cp_private *cp) { diff --git a/drivers/net/8390.c b/drivers/net/8390.c index f8702742008..86be96af9c8 100644 --- a/drivers/net/8390.c +++ b/drivers/net/8390.c @@ -275,12 +275,14 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev); int send_length = skb->len, output_page; unsigned long flags; + char buf[ETH_ZLEN]; + char *data = skb->data; if (skb->len < ETH_ZLEN) { - skb = skb_padto(skb, ETH_ZLEN); - if (skb == NULL) - return 0; + memset(buf, 0, ETH_ZLEN); /* more efficient than doing just the needed bits */ + memcpy(buf, data, skb->len); send_length = ETH_ZLEN; + data = buf; } /* Mask interrupts from the ethercard. @@ -347,7 +349,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) * trigger the send later, upon receiving a Tx done interrupt. */ - ei_block_output(dev, send_length, skb->data, output_page); + ei_block_output(dev, send_length, data, output_page); if (! ei_local->txing) { diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 0c6b45a11d1..39189903e35 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -854,6 +854,17 @@ config SMC9194 <file:Documentation/networking/net-modules.txt>. The module will be called smc9194. +config NET_NETX + tristate "NetX Ethernet support" + select MII + depends on NET_ETHERNET && ARCH_NETX + help + This is support for the Hilscher netX builtin Ethernet ports + + To compile this driver as a module, choose M here and read + <file:Documentation/networking/net-modules.txt>. The module + will be called netx-eth. + config DM9000 tristate "DM9000 support" depends on (ARM || MIPS) && NET_ETHERNET @@ -1376,8 +1387,8 @@ config APRICOT called apricot. config B44 - tristate "Broadcom 4400 ethernet support (EXPERIMENTAL)" - depends on NET_PCI && PCI && EXPERIMENTAL + tristate "Broadcom 4400 ethernet support" + depends on NET_PCI && PCI select MII help If you have a network (Ethernet) controller of this type, say Y and @@ -2190,7 +2201,7 @@ config BNX2 config SPIDER_NET tristate "Spider Gigabit Ethernet driver" - depends on PCI && PPC_CELL + depends on PCI && PPC_IBM_CELL_BLADE select FW_LOADER help This driver supports the Gigabit Ethernet chips present on the @@ -2198,11 +2209,11 @@ config SPIDER_NET config GIANFAR tristate "Gianfar Ethernet" - depends on 85xx || 83xx + depends on 85xx || 83xx || PPC_86xx select PHYLIB help - This driver supports the Gigabit TSEC on the MPC85xx - family of chips, and the FEC on the 8540 + This driver supports the Gigabit TSEC on the MPC83xx, MPC85xx, + and MPC86xx family of chips, and the FEC on the 8540. config GFAR_NAPI bool "NAPI Support" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 1eced328750..c91e95126f7 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -187,6 +187,7 @@ obj-$(CONFIG_MACSONIC) += macsonic.o obj-$(CONFIG_MACMACE) += macmace.o obj-$(CONFIG_MAC89x0) += mac89x0.o obj-$(CONFIG_TUN) += tun.o +obj-$(CONFIG_NET_NETX) += netx-eth.o obj-$(CONFIG_DL2K) += dl2k.o obj-$(CONFIG_R8169) += r8169.o obj-$(CONFIG_AMD8111_ETH) += amd8111e.o diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c index 5503dc8a66e..613005a0285 100644 --- a/drivers/net/arm/at91_ether.c +++ b/drivers/net/arm/at91_ether.c @@ -43,7 +43,9 @@ #define DRV_VERSION "1.0" static struct net_device *at91_dev; -static struct clk *ether_clk; + +static struct timer_list check_timer; +#define LINK_POLL_INTERVAL (HZ) /* ..................................................................... */ @@ -143,7 +145,7 @@ static void read_phy(unsigned char phy_addr, unsigned char address, unsigned int * MAC accordingly. * If no link or auto-negotiation is busy, then no changes are made. */ -static void update_linkspeed(struct net_device *dev) +static void update_linkspeed(struct net_device *dev, int silent) { struct at91_private *lp = (struct at91_private *) dev->priv; unsigned int bmsr, bmcr, lpa, mac_cfg; @@ -151,7 +153,8 @@ static void update_linkspeed(struct net_device *dev) if (!mii_link_ok(&lp->mii)) { /* no link */ netif_carrier_off(dev); - printk(KERN_INFO "%s: Link down.\n", dev->name); + if (!silent) + printk(KERN_INFO "%s: Link down.\n", dev->name); return; } @@ -186,7 +189,8 @@ static void update_linkspeed(struct net_device *dev) } at91_emac_write(AT91_EMAC_CFG, mac_cfg); - printk(KERN_INFO "%s: Link now %i-%s\n", dev->name, speed, (duplex == DUPLEX_FULL) ? "FullDuplex" : "HalfDuplex"); + if (!silent) + printk(KERN_INFO "%s: Link now %i-%s\n", dev->name, speed, (duplex == DUPLEX_FULL) ? "FullDuplex" : "HalfDuplex"); netif_carrier_on(dev); } @@ -226,7 +230,7 @@ static irqreturn_t at91ether_phy_interrupt(int irq, void *dev_id, struct pt_regs goto done; } - update_linkspeed(dev); + update_linkspeed(dev, 0); done: disable_mdi(); @@ -243,14 +247,17 @@ static void enable_phyirq(struct net_device *dev) unsigned int dsintr, irq_number; int status; - if (lp->phy_type == MII_RTL8201_ID) /* RTL8201 does not have an interrupt */ - return; - if (lp->phy_type == MII_DP83847_ID) /* DP83847 does not have an interrupt */ - return; - if (lp->phy_type == MII_AC101L_ID) /* AC101L interrupt not supported yet */ + irq_number = lp->board_data.phy_irq_pin; + if (!irq_number) { + /* + * PHY doesn't have an IRQ pin (RTL8201, DP83847, AC101L), + * or board does not have it connected. + */ + check_timer.expires = jiffies + LINK_POLL_INTERVAL; + add_timer(&check_timer); return; + } - irq_number = lp->board_data.phy_irq_pin; status = request_irq(irq_number, at91ether_phy_interrupt, 0, dev->name, dev); if (status) { printk(KERN_ERR "at91_ether: PHY IRQ %d request failed - status %d!\n", irq_number, status); @@ -292,12 +299,11 @@ static void disable_phyirq(struct net_device *dev) unsigned int dsintr; unsigned int irq_number; - if (lp->phy_type == MII_RTL8201_ID) /* RTL8201 does not have an interrupt */ - return; - if (lp->phy_type == MII_DP83847_ID) /* DP83847 does not have an interrupt */ - return; - if (lp->phy_type == MII_AC101L_ID) /* AC101L interrupt not supported yet */ + irq_number = lp->board_data.phy_irq_pin; + if (!irq_number) { + del_timer_sync(&check_timer); return; + } spin_lock_irq(&lp->lock); enable_mdi(); @@ -326,7 +332,6 @@ static void disable_phyirq(struct net_device *dev) disable_mdi(); spin_unlock_irq(&lp->lock); - irq_number = lp->board_data.phy_irq_pin; free_irq(irq_number, dev); /* Free interrupt handler */ } @@ -355,6 +360,18 @@ static void reset_phy(struct net_device *dev) } #endif +static void at91ether_check_link(unsigned long dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + + enable_mdi(); + update_linkspeed(dev, 1); + disable_mdi(); + + check_timer.expires = jiffies + LINK_POLL_INTERVAL; + add_timer(&check_timer); +} + /* ......................... ADDRESS MANAGEMENT ........................ */ /* @@ -501,7 +518,7 @@ static int hash_get_index(__u8 *addr) hash_index |= (bitval << j); } - return hash_index; + return hash_index; } /* @@ -557,10 +574,8 @@ static void at91ether_set_rx_mode(struct net_device *dev) at91_emac_write(AT91_EMAC_CFG, cfg); } - /* ......................... ETHTOOL SUPPORT ........................... */ - static int mdio_read(struct net_device *dev, int phy_id, int location) { unsigned int value; @@ -642,6 +657,22 @@ static struct ethtool_ops at91ether_ethtool_ops = { .get_link = ethtool_op_get_link, }; +static int at91ether_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct at91_private *lp = (struct at91_private *) dev->priv; + int res; + + if (!netif_running(dev)) + return -EINVAL; + + spin_lock_irq(&lp->lock); + enable_mdi(); + res = generic_mii_ioctl(&lp->mii, if_mii(rq), cmd, NULL); + disable_mdi(); + spin_unlock_irq(&lp->lock); + + return res; +} /* ................................ MAC ................................ */ @@ -685,10 +716,10 @@ static int at91ether_open(struct net_device *dev) struct at91_private *lp = (struct at91_private *) dev->priv; unsigned long ctl; - if (!is_valid_ether_addr(dev->dev_addr)) - return -EADDRNOTAVAIL; + if (!is_valid_ether_addr(dev->dev_addr)) + return -EADDRNOTAVAIL; - clk_enable(ether_clk); /* Re-enable Peripheral clock */ + clk_enable(lp->ether_clk); /* Re-enable Peripheral clock */ /* Clear internal statistics */ ctl = at91_emac_read(AT91_EMAC_CTL); @@ -708,7 +739,7 @@ static int at91ether_open(struct net_device *dev) /* Determine current link speed */ spin_lock_irq(&lp->lock); enable_mdi(); - update_linkspeed(dev); + update_linkspeed(dev, 0); disable_mdi(); spin_unlock_irq(&lp->lock); @@ -722,6 +753,7 @@ static int at91ether_open(struct net_device *dev) */ static int at91ether_close(struct net_device *dev) { + struct at91_private *lp = (struct at91_private *) dev->priv; unsigned long ctl; /* Disable Receiver and Transmitter */ @@ -738,7 +770,7 @@ static int at91ether_close(struct net_device *dev) netif_stop_queue(dev); - clk_disable(ether_clk); /* Disable Peripheral clock */ + clk_disable(lp->ether_clk); /* Disable Peripheral clock */ return 0; } @@ -870,7 +902,7 @@ static irqreturn_t at91ether_interrupt(int irq, void *dev_id, struct pt_regs *re if (intstatus & AT91_EMAC_RCOM) /* Receive complete */ at91ether_rx(dev); - if (intstatus & AT91_EMAC_TCOM) { /* Transmit complete */ + if (intstatus & AT91_EMAC_TCOM) { /* Transmit complete */ /* The TCOM bit is set even if the transmission failed. */ if (intstatus & (AT91_EMAC_TUND | AT91_EMAC_RTRY)) lp->stats.tx_errors += 1; @@ -899,7 +931,8 @@ static irqreturn_t at91ether_interrupt(int irq, void *dev_id, struct pt_regs *re /* * Initialize the ethernet interface */ -static int __init at91ether_setup(unsigned long phy_type, unsigned short phy_address, struct platform_device *pdev) +static int __init at91ether_setup(unsigned long phy_type, unsigned short phy_address, + struct platform_device *pdev, struct clk *ether_clk) { struct at91_eth_data *board_data = pdev->dev.platform_data; struct net_device *dev; @@ -933,6 +966,7 @@ static int __init at91ether_setup(unsigned long phy_type, unsigned short phy_add return -ENOMEM; } lp->board_data = *board_data; + lp->ether_clk = ether_clk; platform_set_drvdata(pdev, dev); spin_lock_init(&lp->lock); @@ -945,6 +979,7 @@ static int __init at91ether_setup(unsigned long phy_type, unsigned short phy_add dev->set_multicast_list = at91ether_set_rx_mode; dev->set_mac_address = set_mac_address; dev->ethtool_ops = &at91ether_ethtool_ops; + dev->do_ioctl = at91ether_ioctl; SET_NETDEV_DEV(dev, &pdev->dev); @@ -975,6 +1010,9 @@ static int __init at91ether_setup(unsigned long phy_type, unsigned short phy_add lp->mii.dev = dev; /* Support for ethtool */ lp->mii.mdio_read = mdio_read; lp->mii.mdio_write = mdio_write; + lp->mii.phy_id = phy_address; + lp->mii.phy_id_mask = 0x1f; + lp->mii.reg_num_mask = 0x1f; lp->phy_type = phy_type; /* Type of PHY connected */ lp->phy_address = phy_address; /* MDI address of PHY */ @@ -992,11 +1030,18 @@ static int __init at91ether_setup(unsigned long phy_type, unsigned short phy_add /* Determine current link speed */ spin_lock_irq(&lp->lock); enable_mdi(); - update_linkspeed(dev); + update_linkspeed(dev, 0); disable_mdi(); spin_unlock_irq(&lp->lock); netif_carrier_off(dev); /* will be enabled in open() */ + /* If board has no PHY IRQ, use a timer to poll the PHY */ + if (!lp->board_data.phy_irq_pin) { + init_timer(&check_timer); + check_timer.data = (unsigned long)dev; + check_timer.function = at91ether_check_link; + } + /* Display ethernet banner */ printk(KERN_INFO "%s: AT91 ethernet at 0x%08x int=%d %s%s (%02x:%02x:%02x:%02x:%02x:%02x)\n", dev->name, (uint) dev->base_addr, dev->irq, @@ -1005,7 +1050,7 @@ static int __init at91ether_setup(unsigned long phy_type, unsigned short phy_add dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); if ((phy_type == MII_DM9161_ID) || (lp->phy_type == MII_DM9161A_ID)) - printk(KERN_INFO "%s: Davicom 9196 PHY %s\n", dev->name, (lp->phy_media == PORT_FIBRE) ? "(Fiber)" : "(Copper)"); + printk(KERN_INFO "%s: Davicom 9161 PHY %s\n", dev->name, (lp->phy_media == PORT_FIBRE) ? "(Fiber)" : "(Copper)"); else if (phy_type == MII_LXT971A_ID) printk(KERN_INFO "%s: Intel LXT971A PHY\n", dev->name); else if (phy_type == MII_RTL8201_ID) @@ -1031,9 +1076,10 @@ static int __init at91ether_probe(struct platform_device *pdev) int detected = -1; unsigned long phy_id; unsigned short phy_address = 0; + struct clk *ether_clk; ether_clk = clk_get(&pdev->dev, "ether_clk"); - if (!ether_clk) { + if (IS_ERR(ether_clk)) { printk(KERN_ERR "at91_ether: no clock defined\n"); return -ENODEV; } @@ -1056,7 +1102,7 @@ static int __init at91ether_probe(struct platform_device *pdev) case MII_DP83847_ID: /* National Semiconductor DP83847: */ case MII_AC101L_ID: /* Altima AC101L: PHY_ID1 = 0x22, PHY_ID2 = 0x5520 */ case MII_KS8721_ID: /* Micrel KS8721: PHY_ID1 = 0x22, PHY_ID2 = 0x1610 */ - detected = at91ether_setup(phy_id, phy_address, pdev); + detected = at91ether_setup(phy_id, phy_address, pdev, ether_clk); break; } @@ -1075,17 +1121,61 @@ static int __devexit at91ether_remove(struct platform_device *pdev) unregister_netdev(at91_dev); free_irq(at91_dev->irq, at91_dev); dma_free_coherent(NULL, sizeof(struct recv_desc_bufs), lp->dlist, (dma_addr_t)lp->dlist_phys); - clk_put(ether_clk); + clk_put(lp->ether_clk); free_netdev(at91_dev); at91_dev = NULL; return 0; } +#ifdef CONFIG_PM + +static int at91ether_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + struct at91_private *lp = (struct at91_private *) at91_dev->priv; + struct net_device *net_dev = platform_get_drvdata(pdev); + int phy_irq = lp->board_data.phy_irq_pin; + + if (netif_running(net_dev)) { + if (phy_irq) + disable_irq(phy_irq); + + netif_stop_queue(net_dev); + netif_device_detach(net_dev); + + clk_disable(lp->ether_clk); + } + return 0; +} + +static int at91ether_resume(struct platform_device *pdev) +{ + struct at91_private *lp = (struct at91_private *) at91_dev->priv; + struct net_device *net_dev = platform_get_drvdata(pdev); + int phy_irq = lp->board_data.phy_irq_pin; + + if (netif_running(net_dev)) { + clk_enable(lp->ether_clk); + + netif_device_attach(net_dev); + netif_start_queue(net_dev); + + if (phy_irq) + enable_irq(phy_irq); + } + return 0; +} + +#else +#define at91ether_suspend NULL +#define at91ether_resume NULL +#endif + static struct platform_driver at91ether_driver = { .probe = at91ether_probe, .remove = __devexit_p(at91ether_remove), - /* FIXME: support suspend and resume */ + .suspend = at91ether_suspend, + .resume = at91ether_resume, .driver = { .name = DRV_NAME, .owner = THIS_MODULE, diff --git a/drivers/net/arm/at91_ether.h b/drivers/net/arm/at91_ether.h index 9885735c9c8..d1e72e02be3 100644 --- a/drivers/net/arm/at91_ether.h +++ b/drivers/net/arm/at91_ether.h @@ -80,6 +80,7 @@ struct at91_private struct net_device_stats stats; struct mii_if_info mii; /* ethtool support */ struct at91_eth_data board_data; /* board-specific configuration */ + struct clk *ether_clk; /* clock */ /* PHY */ unsigned long phy_type; /* type of PHY (PHY_ID) */ diff --git a/drivers/net/b44.c b/drivers/net/b44.c index d8233e0b789..a7e4ba5a580 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -29,8 +29,8 @@ #define DRV_MODULE_NAME "b44" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "1.00" -#define DRV_MODULE_RELDATE "Apr 7, 2006" +#define DRV_MODULE_VERSION "1.01" +#define DRV_MODULE_RELDATE "Jun 16, 2006" #define B44_DEF_MSG_ENABLE \ (NETIF_MSG_DRV | \ @@ -75,6 +75,15 @@ /* minimum number of free TX descriptors required to wake up TX process */ #define B44_TX_WAKEUP_THRESH (B44_TX_RING_SIZE / 4) +/* b44 internal pattern match filter info */ +#define B44_PATTERN_BASE 0x400 +#define B44_PATTERN_SIZE 0x80 +#define B44_PMASK_BASE 0x600 +#define B44_PMASK_SIZE 0x10 +#define B44_MAX_PATTERNS 16 +#define B44_ETHIPV6UDP_HLEN 62 +#define B44_ETHIPV4UDP_HLEN 42 + static char version[] __devinitdata = DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; @@ -101,7 +110,7 @@ MODULE_DEVICE_TABLE(pci, b44_pci_tbl); static void b44_halt(struct b44 *); static void b44_init_rings(struct b44 *); -static void b44_init_hw(struct b44 *); +static void b44_init_hw(struct b44 *, int); static int dma_desc_align_mask; static int dma_desc_sync_size; @@ -873,7 +882,7 @@ static int b44_poll(struct net_device *netdev, int *budget) spin_lock_irq(&bp->lock); b44_halt(bp); b44_init_rings(bp); - b44_init_hw(bp); + b44_init_hw(bp, 1); netif_wake_queue(bp->dev); spin_unlock_irq(&bp->lock); done = 1; @@ -942,7 +951,7 @@ static void b44_tx_timeout(struct net_device *dev) b44_halt(bp); b44_init_rings(bp); - b44_init_hw(bp); + b44_init_hw(bp, 1); spin_unlock_irq(&bp->lock); @@ -1059,7 +1068,7 @@ static int b44_change_mtu(struct net_device *dev, int new_mtu) b44_halt(bp); dev->mtu = new_mtu; b44_init_rings(bp); - b44_init_hw(bp); + b44_init_hw(bp, 1); spin_unlock_irq(&bp->lock); b44_enable_ints(bp); @@ -1356,13 +1365,15 @@ static int b44_set_mac_addr(struct net_device *dev, void *p) * packet processing. Invoked with bp->lock held. */ static void __b44_set_rx_mode(struct net_device *); -static void b44_init_hw(struct b44 *bp) +static void b44_init_hw(struct b44 *bp, int full_reset) { u32 val; b44_chip_reset(bp); - b44_phy_reset(bp); - b44_setup_phy(bp); + if (full_reset) { + b44_phy_reset(bp); + b44_setup_phy(bp); + } /* Enable CRC32, set proper LED modes and power on PHY */ bw32(bp, B44_MAC_CTRL, MAC_CTRL_CRC32_ENAB | MAC_CTRL_PHY_LEDCTRL); @@ -1376,16 +1387,21 @@ static void b44_init_hw(struct b44 *bp) bw32(bp, B44_TXMAXLEN, bp->dev->mtu + ETH_HLEN + 8 + RX_HEADER_LEN); bw32(bp, B44_TX_WMARK, 56); /* XXX magic */ - bw32(bp, B44_DMATX_CTRL, DMATX_CTRL_ENABLE); - bw32(bp, B44_DMATX_ADDR, bp->tx_ring_dma + bp->dma_offset); - bw32(bp, B44_DMARX_CTRL, (DMARX_CTRL_ENABLE | - (bp->rx_offset << DMARX_CTRL_ROSHIFT))); - bw32(bp, B44_DMARX_ADDR, bp->rx_ring_dma + bp->dma_offset); + if (full_reset) { + bw32(bp, B44_DMATX_CTRL, DMATX_CTRL_ENABLE); + bw32(bp, B44_DMATX_ADDR, bp->tx_ring_dma + bp->dma_offset); + bw32(bp, B44_DMARX_CTRL, (DMARX_CTRL_ENABLE | + (bp->rx_offset << DMARX_CTRL_ROSHIFT))); + bw32(bp, B44_DMARX_ADDR, bp->rx_ring_dma + bp->dma_offset); - bw32(bp, B44_DMARX_PTR, bp->rx_pending); - bp->rx_prod = bp->rx_pending; + bw32(bp, B44_DMARX_PTR, bp->rx_pending); + bp->rx_prod = bp->rx_pending; - bw32(bp, B44_MIB_CTRL, MIB_CTRL_CLR_ON_READ); + bw32(bp, B44_MIB_CTRL, MIB_CTRL_CLR_ON_READ); + } else { + bw32(bp, B44_DMARX_CTRL, (DMARX_CTRL_ENABLE | + (bp->rx_offset << DMARX_CTRL_ROSHIFT))); + } val = br32(bp, B44_ENET_CTRL); bw32(bp, B44_ENET_CTRL, (val | ENET_CTRL_ENABLE)); @@ -1401,7 +1417,7 @@ static int b44_open(struct net_device *dev) goto out; b44_init_rings(bp); - b44_init_hw(bp); + b44_init_hw(bp, 1); b44_check_phy(bp); @@ -1450,6 +1466,140 @@ static void b44_poll_controller(struct net_device *dev) } #endif +static void bwfilter_table(struct b44 *bp, u8 *pp, u32 bytes, u32 table_offset) +{ + u32 i; + u32 *pattern = (u32 *) pp; + + for (i = 0; i < bytes; i += sizeof(u32)) { + bw32(bp, B44_FILT_ADDR, table_offset + i); + bw32(bp, B44_FILT_DATA, pattern[i / sizeof(u32)]); + } +} + +static int b44_magic_pattern(u8 *macaddr, u8 *ppattern, u8 *pmask, int offset) +{ + int magicsync = 6; + int k, j, len = offset; + int ethaddr_bytes = ETH_ALEN; + + memset(ppattern + offset, 0xff, magicsync); + for (j = 0; j < magicsync; j++) + set_bit(len++, (unsigned long *) pmask); + + for (j = 0; j < B44_MAX_PATTERNS; j++) { + if ((B44_PATTERN_SIZE - len) >= ETH_ALEN) + ethaddr_bytes = ETH_ALEN; + else + ethaddr_bytes = B44_PATTERN_SIZE - len; + if (ethaddr_bytes <=0) + break; + for (k = 0; k< ethaddr_bytes; k++) { + ppattern[offset + magicsync + + (j * ETH_ALEN) + k] = macaddr[k]; + len++; + set_bit(len, (unsigned long *) pmask); + } + } + return len - 1; +} + +/* Setup magic packet patterns in the b44 WOL + * pattern matching filter. + */ +static void b44_setup_pseudo_magicp(struct b44 *bp) +{ + + u32 val; + int plen0, plen1, plen2; + u8 *pwol_pattern; + u8 pwol_mask[B44_PMASK_SIZE]; + + pwol_pattern = kmalloc(B44_PATTERN_SIZE, GFP_KERNEL); + if (!pwol_pattern) { + printk(KERN_ERR PFX "Memory not available for WOL\n"); + return; + } + + /* Ipv4 magic packet pattern - pattern 0.*/ + memset(pwol_pattern, 0, B44_PATTERN_SIZE); + memset(pwol_mask, 0, B44_PMASK_SIZE); + plen0 = b44_magic_pattern(bp->dev->dev_addr, pwol_pattern, pwol_mask, + B44_ETHIPV4UDP_HLEN); + + bwfilter_table(bp, pwol_pattern, B44_PATTERN_SIZE, B44_PATTERN_BASE); + bwfilter_table(bp, pwol_mask, B44_PMASK_SIZE, B44_PMASK_BASE); + + /* Raw ethernet II magic packet pattern - pattern 1 */ + memset(pwol_pattern, 0, B44_PATTERN_SIZE); + memset(pwol_mask, 0, B44_PMASK_SIZE); + plen1 = b44_magic_pattern(bp->dev->dev_addr, pwol_pattern, pwol_mask, + ETH_HLEN); + + bwfilter_table(bp, pwol_pattern, B44_PATTERN_SIZE, + B44_PATTERN_BASE + B44_PATTERN_SIZE); + bwfilter_table(bp, pwol_mask, B44_PMASK_SIZE, + B44_PMASK_BASE + B44_PMASK_SIZE); + + /* Ipv6 magic packet pattern - pattern 2 */ + memset(pwol_pattern, 0, B44_PATTERN_SIZE); + memset(pwol_mask, 0, B44_PMASK_SIZE); + plen2 = b44_magic_pattern(bp->dev->dev_addr, pwol_pattern, pwol_mask, + B44_ETHIPV6UDP_HLEN); + + bwfilter_table(bp, pwol_pattern, B44_PATTERN_SIZE, + B44_PATTERN_BASE + B44_PATTERN_SIZE + B44_PATTERN_SIZE); + bwfilter_table(bp, pwol_mask, B44_PMASK_SIZE, + B44_PMASK_BASE + B44_PMASK_SIZE + B44_PMASK_SIZE); + + kfree(pwol_pattern); + + /* set these pattern's lengths: one less than each real length */ + val = plen0 | (plen1 << 8) | (plen2 << 16) | WKUP_LEN_ENABLE_THREE; + bw32(bp, B44_WKUP_LEN, val); + + /* enable wakeup pattern matching */ + val = br32(bp, B44_DEVCTRL); + bw32(bp, B44_DEVCTRL, val | DEVCTRL_PFE); + +} + +static void b44_setup_wol(struct b44 *bp) +{ + u32 val; + u16 pmval; + + bw32(bp, B44_RXCONFIG, RXCONFIG_ALLMULTI); + + if (bp->flags & B44_FLAG_B0_ANDLATER) { + + bw32(bp, B44_WKUP_LEN, WKUP_LEN_DISABLE); + + val = bp->dev->dev_addr[2] << 24 | + bp->dev->dev_addr[3] << 16 | + bp->dev->dev_addr[4] << 8 | + bp->dev->dev_addr[5]; + bw32(bp, B44_ADDR_LO, val); + + val = bp->dev->dev_addr[0] << 8 | + bp->dev->dev_addr[1]; + bw32(bp, B44_ADDR_HI, val); + + val = br32(bp, B44_DEVCTRL); + bw32(bp, B44_DEVCTRL, val | DEVCTRL_MPM | DEVCTRL_PFE); + + } else { + b44_setup_pseudo_magicp(bp); + } + + val = br32(bp, B44_SBTMSLOW); + bw32(bp, B44_SBTMSLOW, val | SBTMSLOW_PE); + + pci_read_config_word(bp->pdev, SSB_PMCSR, &pmval); + pci_write_config_word(bp->pdev, SSB_PMCSR, pmval | SSB_PE); + +} + static int b44_close(struct net_device *dev) { struct b44 *bp = netdev_priv(dev); @@ -1475,6 +1625,11 @@ static int b44_close(struct net_device *dev) netif_poll_enable(dev); + if (bp->flags & B44_FLAG_WOL_ENABLE) { + b44_init_hw(bp, 0); + b44_setup_wol(bp); + } + b44_free_consistent(bp); return 0; @@ -1620,8 +1775,6 @@ static int b44_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct b44 *bp = netdev_priv(dev); - if (!netif_running(dev)) - return -EAGAIN; cmd->supported = (SUPPORTED_Autoneg); cmd->supported |= (SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | @@ -1649,6 +1802,12 @@ static int b44_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) XCVR_INTERNAL : XCVR_EXTERNAL; cmd->autoneg = (bp->flags & B44_FLAG_FORCE_LINK) ? AUTONEG_DISABLE : AUTONEG_ENABLE; + if (cmd->autoneg == AUTONEG_ENABLE) + cmd->advertising |= ADVERTISED_Autoneg; + if (!netif_running(dev)){ + cmd->speed = 0; + cmd->duplex = 0xff; + } cmd->maxtxpkt = 0; cmd->maxrxpkt = 0; return 0; @@ -1658,9 +1817,6 @@ static int b44_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct b44 *bp = netdev_priv(dev); - if (!netif_running(dev)) - return -EAGAIN; - /* We do not support gigabit. */ if (cmd->autoneg == AUTONEG_ENABLE) { if (cmd->advertising & @@ -1677,28 +1833,39 @@ static int b44_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) spin_lock_irq(&bp->lock); if (cmd->autoneg == AUTONEG_ENABLE) { - bp->flags &= ~B44_FLAG_FORCE_LINK; - bp->flags &= ~(B44_FLAG_ADV_10HALF | + bp->flags &= ~(B44_FLAG_FORCE_LINK | + B44_FLAG_100_BASE_T | + B44_FLAG_FULL_DUPLEX | + B44_FLAG_ADV_10HALF | B44_FLAG_ADV_10FULL | B44_FLAG_ADV_100HALF | B44_FLAG_ADV_100FULL); - if (cmd->advertising & ADVERTISE_10HALF) - bp->flags |= B44_FLAG_ADV_10HALF; - if (cmd->advertising & ADVERTISE_10FULL) - bp->flags |= B44_FLAG_ADV_10FULL; - if (cmd->advertising & ADVERTISE_100HALF) - bp->flags |= B44_FLAG_ADV_100HALF; - if (cmd->advertising & ADVERTISE_100FULL) - bp->flags |= B44_FLAG_ADV_100FULL; + if (cmd->advertising == 0) { + bp->flags |= (B44_FLAG_ADV_10HALF | + B44_FLAG_ADV_10FULL | + B44_FLAG_ADV_100HALF | + B44_FLAG_ADV_100FULL); + } else { + if (cmd->advertising & ADVERTISED_10baseT_Half) + bp->flags |= B44_FLAG_ADV_10HALF; + if (cmd->advertising & ADVERTISED_10baseT_Full) + bp->flags |= B44_FLAG_ADV_10FULL; + if (cmd->advertising & ADVERTISED_100baseT_Half) + bp->flags |= B44_FLAG_ADV_100HALF; + if (cmd->advertising & ADVERTISED_100baseT_Full) + bp->flags |= B44_FLAG_ADV_100FULL; + } } else { bp->flags |= B44_FLAG_FORCE_LINK; + bp->flags &= ~(B44_FLAG_100_BASE_T | B44_FLAG_FULL_DUPLEX); if (cmd->speed == SPEED_100) bp->flags |= B44_FLAG_100_BASE_T; if (cmd->duplex == DUPLEX_FULL) bp->flags |= B44_FLAG_FULL_DUPLEX; } - b44_setup_phy(bp); + if (netif_running(dev)) + b44_setup_phy(bp); spin_unlock_irq(&bp->lock); @@ -1734,7 +1901,7 @@ static int b44_set_ringparam(struct net_device *dev, b44_halt(bp); b44_init_rings(bp); - b44_init_hw(bp); + b44_init_hw(bp, 1); netif_wake_queue(bp->dev); spin_unlock_irq(&bp->lock); @@ -1777,7 +1944,7 @@ static int b44_set_pauseparam(struct net_device *dev, if (bp->flags & B44_FLAG_PAUSE_AUTO) { b44_halt(bp); b44_init_rings(bp); - b44_init_hw(bp); + b44_init_hw(bp, 1); } else { __b44_set_flow_ctrl(bp, bp->flags); } @@ -1819,12 +1986,40 @@ static void b44_get_ethtool_stats(struct net_device *dev, spin_unlock_irq(&bp->lock); } +static void b44_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct b44 *bp = netdev_priv(dev); + + wol->supported = WAKE_MAGIC; + if (bp->flags & B44_FLAG_WOL_ENABLE) + wol->wolopts = WAKE_MAGIC; + else + wol->wolopts = 0; + memset(&wol->sopass, 0, sizeof(wol->sopass)); +} + +static int b44_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct b44 *bp = netdev_priv(dev); + + spin_lock_irq(&bp->lock); + if (wol->wolopts & WAKE_MAGIC) + bp->flags |= B44_FLAG_WOL_ENABLE; + else + bp->flags &= ~B44_FLAG_WOL_ENABLE; + spin_unlock_irq(&bp->lock); + + return 0; +} + static struct ethtool_ops b44_ethtool_ops = { .get_drvinfo = b44_get_drvinfo, .get_settings = b44_get_settings, .set_settings = b44_set_settings, .nway_reset = b44_nway_reset, .get_link = ethtool_op_get_link, + .get_wol = b44_get_wol, + .set_wol = b44_set_wol, .get_ringparam = b44_get_ringparam, .set_ringparam = b44_set_ringparam, .get_pauseparam = b44_get_pauseparam, @@ -1903,6 +2098,10 @@ static int __devinit b44_get_invariants(struct b44 *bp) /* XXX - really required? bp->flags |= B44_FLAG_BUGGY_TXPTR; */ + + if (ssb_get_core_rev(bp) >= 7) + bp->flags |= B44_FLAG_B0_ANDLATER; + out: return err; } @@ -2103,6 +2302,10 @@ static int b44_suspend(struct pci_dev *pdev, pm_message_t state) spin_unlock_irq(&bp->lock); free_irq(dev->irq, dev); + if (bp->flags & B44_FLAG_WOL_ENABLE) { + b44_init_hw(bp, 0); + b44_setup_wol(bp); + } pci_disable_device(pdev); return 0; } @@ -2125,7 +2328,7 @@ static int b44_resume(struct pci_dev *pdev) spin_lock_irq(&bp->lock); b44_init_rings(bp); - b44_init_hw(bp); + b44_init_hw(bp, 1); netif_device_attach(bp->dev); spin_unlock_irq(&bp->lock); diff --git a/drivers/net/b44.h b/drivers/net/b44.h index b178662978f..4944507fad2 100644 --- a/drivers/net/b44.h +++ b/drivers/net/b44.h @@ -24,6 +24,9 @@ #define WKUP_LEN_P3_MASK 0x7f000000 /* Pattern 3 */ #define WKUP_LEN_P3_SHIFT 24 #define WKUP_LEN_D3 0x80000000 +#define WKUP_LEN_DISABLE 0x80808080 +#define WKUP_LEN_ENABLE_TWO 0x80800000 +#define WKUP_LEN_ENABLE_THREE 0x80000000 #define B44_ISTAT 0x0020UL /* Interrupt Status */ #define ISTAT_LS 0x00000020 /* Link Change (B0 only) */ #define ISTAT_PME 0x00000040 /* Power Management Event */ @@ -264,6 +267,8 @@ #define SBIDHIGH_VC_SHIFT 16 /* SSB PCI config space registers. */ +#define SSB_PMCSR 0x44 +#define SSB_PE 0x100 #define SSB_BAR0_WIN 0x80 #define SSB_BAR1_WIN 0x84 #define SSB_SPROM_CONTROL 0x88 @@ -420,6 +425,7 @@ struct b44 { u32 dma_offset; u32 flags; +#define B44_FLAG_B0_ANDLATER 0x00000001 #define B44_FLAG_BUGGY_TXPTR 0x00000002 #define B44_FLAG_REORDER_BUG 0x00000004 #define B44_FLAG_PAUSE_AUTO 0x00008000 @@ -435,6 +441,7 @@ struct b44 { #define B44_FLAG_INTERNAL_PHY 0x10000000 #define B44_FLAG_RX_RING_HACK 0x20000000 #define B44_FLAG_TX_RING_HACK 0x40000000 +#define B44_FLAG_WOL_ENABLE 0x80000000 u32 rx_offset; diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 62b38a4494b..191383d461d 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -2076,7 +2076,7 @@ static void nv_set_multicast(struct net_device *dev) spin_unlock_irq(&np->lock); } -void nv_update_pause(struct net_device *dev, u32 pause_flags) +static void nv_update_pause(struct net_device *dev, u32 pause_flags) { struct fe_priv *np = netdev_priv(dev); u8 __iomem *base = get_hwbase(dev); diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index 666346f6469..4c2e7279ba3 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -61,7 +61,7 @@ #undef DEBUG #define ibmveth_printk(fmt, args...) \ - printk(KERN_INFO "%s: " fmt, __FILE__, ## args) + printk(KERN_DEBUG "%s: " fmt, __FILE__, ## args) #define ibmveth_error_printk(fmt, args...) \ printk(KERN_ERR "(%s:%3.3d ua:%x) ERROR: " fmt, __FILE__, __LINE__ , adapter->vdev->unit_address, ## args) diff --git a/drivers/net/ioc3-eth.c b/drivers/net/ioc3-eth.c index ae71ed57c12..e76e6e7be0b 100644 --- a/drivers/net/ioc3-eth.c +++ b/drivers/net/ioc3-eth.c @@ -145,7 +145,7 @@ static inline struct sk_buff * ioc3_alloc_skb(unsigned long length, static inline unsigned long ioc3_map(void *ptr, unsigned long vdev) { #ifdef CONFIG_SGI_IP27 - vdev <<= 58; /* Shift to PCI64_ATTR_VIRTUAL */ + vdev <<= 57; /* Shift to PCI64_ATTR_VIRTUAL */ return vdev | (0xaUL << PCI64_ATTR_TARG_SHFT) | PCI64_ATTR_PREF | ((unsigned long)ptr & TO_PHYS_MASK); diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index f0f04be989d..93394d76587 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -69,6 +69,7 @@ #include <linux/delay.h> #include <linux/mm.h> #include <linux/ethtool.h> +#include <linux/if_ether.h> #include <asm/abs_addr.h> #include <asm/iseries/mf.h> @@ -1035,11 +1036,22 @@ static struct ethtool_ops ops = { .get_link = veth_get_link, }; -static struct net_device * __init veth_probe_one(int vlan, struct device *vdev) +static struct net_device * __init veth_probe_one(int vlan, + struct vio_dev *vio_dev) { struct net_device *dev; struct veth_port *port; + struct device *vdev = &vio_dev->dev; int i, rc; + const unsigned char *mac_addr; + + mac_addr = vio_get_attribute(vio_dev, "local-mac-address", NULL); + if (mac_addr == NULL) + mac_addr = vio_get_attribute(vio_dev, "mac-address", NULL); + if (mac_addr == NULL) { + veth_error("Unable to fetch MAC address from device tree.\n"); + return NULL; + } dev = alloc_etherdev(sizeof (struct veth_port)); if (! dev) { @@ -1064,16 +1076,11 @@ static struct net_device * __init veth_probe_one(int vlan, struct device *vdev) } port->dev = vdev; - dev->dev_addr[0] = 0x02; - dev->dev_addr[1] = 0x01; - dev->dev_addr[2] = 0xff; - dev->dev_addr[3] = vlan; - dev->dev_addr[4] = 0xff; - dev->dev_addr[5] = this_lp; + memcpy(dev->dev_addr, mac_addr, ETH_ALEN); dev->mtu = VETH_MAX_MTU; - memcpy(&port->mac_addr, dev->dev_addr, 6); + memcpy(&port->mac_addr, mac_addr, ETH_ALEN); dev->open = veth_open; dev->hard_start_xmit = veth_start_xmit; @@ -1608,7 +1615,7 @@ static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id) struct net_device *dev; struct veth_port *port; - dev = veth_probe_one(i, &vdev->dev); + dev = veth_probe_one(i, vdev); if (dev == NULL) { veth_remove(vdev); return 1; @@ -1641,7 +1648,7 @@ static int veth_probe(struct vio_dev *vdev, const struct vio_device_id *id) * support. */ static struct vio_device_id veth_device_table[] __devinitdata = { - { "vlan", "" }, + { "network", "IBM,iSeries-l-lan" }, { "", "" } }; MODULE_DEVICE_TABLE(vio, veth_device_table); diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index e1feb58bd66..5a74f63618b 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -2120,7 +2120,7 @@ abort_linearize: goto drop; } - if (skb_linearize(skb, GFP_ATOMIC)) + if (skb_linearize(skb)) goto drop; mgp->tx_linearized++; @@ -2251,12 +2251,6 @@ static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp) } cap = pci_find_ext_capability(bridge, PCI_EXT_CAP_ID_ERR); - /* nvidia ext cap is not always linked in ext cap chain */ - if (!cap - && bridge->vendor == PCI_VENDOR_ID_NVIDIA - && bridge->device == PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_PCIE) - cap = 0x160; - if (!cap) return; @@ -2732,8 +2726,6 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* Save configuration space to be restored if the * nic resets due to a parity error */ myri10ge_save_state(mgp); - /* Restore state immediately since pci_save_msi_state disables MSI */ - myri10ge_restore_state(mgp); /* Setup the watchdog timer */ setup_timer(&mgp->watchdog_timer, myri10ge_watchdog_timer, diff --git a/drivers/net/netx-eth.c b/drivers/net/netx-eth.c new file mode 100644 index 00000000000..b92430c4e3a --- /dev/null +++ b/drivers/net/netx-eth.c @@ -0,0 +1,516 @@ +/* + * drivers/net/netx-eth.c + * + * Copyright (c) 2005 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> + +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/mii.h> + +#include <asm/io.h> +#include <asm/hardware.h> +#include <asm/arch/hardware.h> +#include <asm/arch/netx-regs.h> +#include <asm/arch/pfifo.h> +#include <asm/arch/xc.h> +#include <asm/arch/eth.h> + +/* XC Fifo Offsets */ +#define EMPTY_PTR_FIFO(xcno) (0 + ((xcno) << 3)) /* Index of the empty pointer FIFO */ +#define IND_FIFO_PORT_HI(xcno) (1 + ((xcno) << 3)) /* Index of the FIFO where received */ + /* Data packages are indicated by XC */ +#define IND_FIFO_PORT_LO(xcno) (2 + ((xcno) << 3)) /* Index of the FIFO where received */ + /* Data packages are indicated by XC */ +#define REQ_FIFO_PORT_HI(xcno) (3 + ((xcno) << 3)) /* Index of the FIFO where Data packages */ + /* have to be indicated by ARM which */ + /* shall be sent */ +#define REQ_FIFO_PORT_LO(xcno) (4 + ((xcno) << 3)) /* Index of the FIFO where Data packages */ + /* have to be indicated by ARM which shall */ + /* be sent */ +#define CON_FIFO_PORT_HI(xcno) (5 + ((xcno) << 3)) /* Index of the FIFO where sent Data packages */ + /* are confirmed */ +#define CON_FIFO_PORT_LO(xcno) (6 + ((xcno) << 3)) /* Index of the FIFO where sent Data */ + /* packages are confirmed */ +#define PFIFO_MASK(xcno) (0x7f << (xcno*8)) + +#define FIFO_PTR_FRAMELEN_SHIFT 0 +#define FIFO_PTR_FRAMELEN_MASK (0x7ff << 0) +#define FIFO_PTR_FRAMELEN(len) (((len) << 0) & FIFO_PTR_FRAMELEN_MASK) +#define FIFO_PTR_TIMETRIG (1<<11) +#define FIFO_PTR_MULTI_REQ +#define FIFO_PTR_ORIGIN (1<<14) +#define FIFO_PTR_VLAN (1<<15) +#define FIFO_PTR_FRAMENO_SHIFT 16 +#define FIFO_PTR_FRAMENO_MASK (0x3f << 16) +#define FIFO_PTR_FRAMENO(no) (((no) << 16) & FIFO_PTR_FRAMENO_MASK) +#define FIFO_PTR_SEGMENT_SHIFT 22 +#define FIFO_PTR_SEGMENT_MASK (0xf << 22) +#define FIFO_PTR_SEGMENT(seg) (((seg) & 0xf) << 22) +#define FIFO_PTR_ERROR_SHIFT 28 +#define FIFO_PTR_ERROR_MASK (0xf << 28) + +#define ISR_LINK_STATUS_CHANGE (1<<4) +#define ISR_IND_LO (1<<3) +#define ISR_CON_LO (1<<2) +#define ISR_IND_HI (1<<1) +#define ISR_CON_HI (1<<0) + +#define ETH_MAC_LOCAL_CONFIG 0x1560 +#define ETH_MAC_4321 0x1564 +#define ETH_MAC_65 0x1568 + +#define MAC_TRAFFIC_CLASS_ARRANGEMENT_SHIFT 16 +#define MAC_TRAFFIC_CLASS_ARRANGEMENT_MASK (0xf<<MAC_TRAFFIC_CLASS_ARRANGEMENT_SHIFT) +#define MAC_TRAFFIC_CLASS_ARRANGEMENT(x) (((x)<<MAC_TRAFFIC_CLASS_ARRANGEMENT_SHIFT) & MAC_TRAFFIC_CLASS_ARRANGEMENT_MASK) +#define LOCAL_CONFIG_LINK_STATUS_IRQ_EN (1<<24) +#define LOCAL_CONFIG_CON_LO_IRQ_EN (1<<23) +#define LOCAL_CONFIG_CON_HI_IRQ_EN (1<<22) +#define LOCAL_CONFIG_IND_LO_IRQ_EN (1<<21) +#define LOCAL_CONFIG_IND_HI_IRQ_EN (1<<20) + +#define CARDNAME "netx-eth" + +/* LSB must be zero */ +#define INTERNAL_PHY_ADR 0x1c + +struct netx_eth_priv { + void __iomem *sram_base, *xpec_base, *xmac_base; + int id; + struct net_device_stats stats; + struct mii_if_info mii; + u32 msg_enable; + struct xc *xc; + spinlock_t lock; +}; + +static void netx_eth_set_multicast_list(struct net_device *ndev) +{ + /* implement me */ +} + +static int +netx_eth_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct netx_eth_priv *priv = netdev_priv(ndev); + unsigned char *buf = skb->data; + unsigned int len = skb->len; + + spin_lock_irq(&priv->lock); + memcpy_toio(priv->sram_base + 1560, (void *)buf, len); + if (len < 60) { + memset_io(priv->sram_base + 1560 + len, 0, 60 - len); + len = 60; + } + + pfifo_push(REQ_FIFO_PORT_LO(priv->id), + FIFO_PTR_SEGMENT(priv->id) | + FIFO_PTR_FRAMENO(1) | + FIFO_PTR_FRAMELEN(len)); + + ndev->trans_start = jiffies; + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + + netif_stop_queue(ndev); + spin_unlock_irq(&priv->lock); + dev_kfree_skb(skb); + + return 0; +} + +static void netx_eth_receive(struct net_device *ndev) +{ + struct netx_eth_priv *priv = netdev_priv(ndev); + unsigned int val, frameno, seg, len; + unsigned char *data; + struct sk_buff *skb; + + val = pfifo_pop(IND_FIFO_PORT_LO(priv->id)); + + frameno = (val & FIFO_PTR_FRAMENO_MASK) >> FIFO_PTR_FRAMENO_SHIFT; + seg = (val & FIFO_PTR_SEGMENT_MASK) >> FIFO_PTR_SEGMENT_SHIFT; + len = (val & FIFO_PTR_FRAMELEN_MASK) >> FIFO_PTR_FRAMELEN_SHIFT; + + skb = dev_alloc_skb(len); + if (unlikely(skb == NULL)) { + printk(KERN_NOTICE "%s: Low memory, packet dropped.\n", + ndev->name); + priv->stats.rx_dropped++; + return; + } + + data = skb_put(skb, len); + + memcpy_fromio(data, priv->sram_base + frameno * 1560, len); + + pfifo_push(EMPTY_PTR_FIFO(priv->id), + FIFO_PTR_SEGMENT(seg) | FIFO_PTR_FRAMENO(frameno)); + + ndev->last_rx = jiffies; + skb->dev = ndev; + skb->protocol = eth_type_trans(skb, ndev); + netif_rx(skb); + priv->stats.rx_packets++; + priv->stats.rx_bytes += len; +} + +static irqreturn_t +netx_eth_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *ndev = dev_id; + struct netx_eth_priv *priv = netdev_priv(ndev); + int status; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + status = readl(NETX_PFIFO_XPEC_ISR(priv->id)); + while (status) { + int fill_level; + writel(status, NETX_PFIFO_XPEC_ISR(priv->id)); + + if ((status & ISR_CON_HI) || (status & ISR_IND_HI)) + printk("%s: unexpected status: 0x%08x\n", + __FUNCTION__, status); + + fill_level = + readl(NETX_PFIFO_FILL_LEVEL(IND_FIFO_PORT_LO(priv->id))); + while (fill_level--) + netx_eth_receive(ndev); + + if (status & ISR_CON_LO) + netif_wake_queue(ndev); + + if (status & ISR_LINK_STATUS_CHANGE) + mii_check_media(&priv->mii, netif_msg_link(priv), 1); + + status = readl(NETX_PFIFO_XPEC_ISR(priv->id)); + } + spin_unlock_irqrestore(&priv->lock, flags); + return IRQ_HANDLED; +} + +static struct net_device_stats *netx_eth_query_statistics(struct net_device *ndev) +{ + struct netx_eth_priv *priv = netdev_priv(ndev); + return &priv->stats; +} + +static int netx_eth_open(struct net_device *ndev) +{ + struct netx_eth_priv *priv = netdev_priv(ndev); + + if (request_irq + (ndev->irq, &netx_eth_interrupt, SA_SHIRQ, ndev->name, ndev)) + return -EAGAIN; + + writel(ndev->dev_addr[0] | + ndev->dev_addr[1]<<8 | + ndev->dev_addr[2]<<16 | + ndev->dev_addr[3]<<24, + priv->xpec_base + NETX_XPEC_RAM_START_OFS + ETH_MAC_4321); + writel(ndev->dev_addr[4] | + ndev->dev_addr[5]<<8, + priv->xpec_base + NETX_XPEC_RAM_START_OFS + ETH_MAC_65); + + writel(LOCAL_CONFIG_LINK_STATUS_IRQ_EN | + LOCAL_CONFIG_CON_LO_IRQ_EN | + LOCAL_CONFIG_CON_HI_IRQ_EN | + LOCAL_CONFIG_IND_LO_IRQ_EN | + LOCAL_CONFIG_IND_HI_IRQ_EN, + priv->xpec_base + NETX_XPEC_RAM_START_OFS + + ETH_MAC_LOCAL_CONFIG); + + mii_check_media(&priv->mii, netif_msg_link(priv), 1); + netif_start_queue(ndev); + + return 0; +} + +static int netx_eth_close(struct net_device *ndev) +{ + struct netx_eth_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + + writel(0, + priv->xpec_base + NETX_XPEC_RAM_START_OFS + ETH_MAC_LOCAL_CONFIG); + + free_irq(ndev->irq, ndev); + + return 0; +} + +static void netx_eth_timeout(struct net_device *ndev) +{ + struct netx_eth_priv *priv = netdev_priv(ndev); + int i; + + printk(KERN_ERR "%s: transmit timed out, resetting\n", ndev->name); + + spin_lock_irq(&priv->lock); + + xc_reset(priv->xc); + xc_start(priv->xc); + + for (i=2; i<=18; i++) + pfifo_push(EMPTY_PTR_FIFO(priv->id), + FIFO_PTR_FRAMENO(i) | FIFO_PTR_SEGMENT(priv->id)); + + spin_unlock_irq(&priv->lock); + + netif_wake_queue(ndev); +} + +static int +netx_eth_phy_read(struct net_device *ndev, int phy_id, int reg) +{ + unsigned int val; + + val = MIIMU_SNRDY | MIIMU_PREAMBLE | MIIMU_PHYADDR(phy_id) | + MIIMU_REGADDR(reg) | MIIMU_PHY_NRES; + + writel(val, NETX_MIIMU); + while (readl(NETX_MIIMU) & MIIMU_SNRDY); + + return readl(NETX_MIIMU) >> 16; + +} + +static void +netx_eth_phy_write(struct net_device *ndev, int phy_id, int reg, int value) +{ + unsigned int val; + + val = MIIMU_SNRDY | MIIMU_PREAMBLE | MIIMU_PHYADDR(phy_id) | + MIIMU_REGADDR(reg) | MIIMU_PHY_NRES | MIIMU_OPMODE_WRITE | + MIIMU_DATA(value); + + writel(val, NETX_MIIMU); + while (readl(NETX_MIIMU) & MIIMU_SNRDY); +} + +static int netx_eth_enable(struct net_device *ndev) +{ + struct netx_eth_priv *priv = netdev_priv(ndev); + unsigned int mac4321, mac65; + int running, i; + + ether_setup(ndev); + + ndev->open = netx_eth_open; + ndev->stop = netx_eth_close; + ndev->hard_start_xmit = netx_eth_hard_start_xmit; + ndev->tx_timeout = netx_eth_timeout; + ndev->watchdog_timeo = msecs_to_jiffies(5000); + ndev->get_stats = netx_eth_query_statistics; + ndev->set_multicast_list = netx_eth_set_multicast_list; + + priv->msg_enable = NETIF_MSG_LINK; + priv->mii.phy_id_mask = 0x1f; + priv->mii.reg_num_mask = 0x1f; + priv->mii.force_media = 0; + priv->mii.full_duplex = 0; + priv->mii.dev = ndev; + priv->mii.mdio_read = netx_eth_phy_read; + priv->mii.mdio_write = netx_eth_phy_write; + priv->mii.phy_id = INTERNAL_PHY_ADR + priv->id; + + running = xc_running(priv->xc); + xc_stop(priv->xc); + + /* if the xc engine is already running, assume the bootloader has + * loaded the firmware for us + */ + if (running) { + /* get Node Address from hardware */ + mac4321 = readl(priv->xpec_base + + NETX_XPEC_RAM_START_OFS + ETH_MAC_4321); + mac65 = readl(priv->xpec_base + + NETX_XPEC_RAM_START_OFS + ETH_MAC_65); + + ndev->dev_addr[0] = mac4321 & 0xff; + ndev->dev_addr[1] = (mac4321 >> 8) & 0xff; + ndev->dev_addr[2] = (mac4321 >> 16) & 0xff; + ndev->dev_addr[3] = (mac4321 >> 24) & 0xff; + ndev->dev_addr[4] = mac65 & 0xff; + ndev->dev_addr[5] = (mac65 >> 8) & 0xff; + } else { + if (xc_request_firmware(priv->xc)) { + printk(CARDNAME ": requesting firmware failed\n"); + return -ENODEV; + } + } + + xc_reset(priv->xc); + xc_start(priv->xc); + + if (!is_valid_ether_addr(ndev->dev_addr)) + printk("%s: Invalid ethernet MAC address. Please " + "set using ifconfig\n", ndev->name); + + for (i=2; i<=18; i++) + pfifo_push(EMPTY_PTR_FIFO(priv->id), + FIFO_PTR_FRAMENO(i) | FIFO_PTR_SEGMENT(priv->id)); + + return register_netdev(ndev); + +} + +static int netx_eth_drv_probe(struct platform_device *pdev) +{ + struct netx_eth_priv *priv; + struct net_device *ndev; + struct netxeth_platform_data *pdata; + int ret; + + ndev = alloc_etherdev(sizeof (struct netx_eth_priv)); + if (!ndev) { + printk("%s: could not allocate device.\n", CARDNAME); + ret = -ENOMEM; + goto exit; + } + SET_MODULE_OWNER(ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + + platform_set_drvdata(pdev, ndev); + + priv = netdev_priv(ndev); + + pdata = (struct netxeth_platform_data *)pdev->dev.platform_data; + priv->xc = request_xc(pdata->xcno, &pdev->dev); + if (!priv->xc) { + dev_err(&pdev->dev, "unable to request xc engine\n"); + ret = -ENODEV; + goto exit_free_netdev; + } + + ndev->irq = priv->xc->irq; + priv->id = pdev->id; + priv->xpec_base = priv->xc->xpec_base; + priv->xmac_base = priv->xc->xmac_base; + priv->sram_base = priv->xc->sram_base; + + ret = pfifo_request(PFIFO_MASK(priv->id)); + if (ret) { + printk("unable to request PFIFO\n"); + goto exit_free_xc; + } + + ret = netx_eth_enable(ndev); + if (ret) + goto exit_free_pfifo; + + return 0; +exit_free_pfifo: + pfifo_free(PFIFO_MASK(priv->id)); +exit_free_xc: + free_xc(priv->xc); +exit_free_netdev: + platform_set_drvdata(pdev, NULL); + free_netdev(ndev); +exit: + return ret; +} + +static int netx_eth_drv_remove(struct platform_device *pdev) +{ + struct net_device *ndev = dev_get_drvdata(&pdev->dev); + struct netx_eth_priv *priv = netdev_priv(ndev); + + platform_set_drvdata(pdev, NULL); + + unregister_netdev(ndev); + xc_stop(priv->xc); + free_xc(priv->xc); + free_netdev(ndev); + pfifo_free(PFIFO_MASK(priv->id)); + + return 0; +} + +static int netx_eth_drv_suspend(struct platform_device *pdev, pm_message_t state) +{ + dev_err(&pdev->dev, "suspend not implemented\n"); + return 0; +} + +static int netx_eth_drv_resume(struct platform_device *pdev) +{ + dev_err(&pdev->dev, "resume not implemented\n"); + return 0; +} + +static struct platform_driver netx_eth_driver = { + .probe = netx_eth_drv_probe, + .remove = netx_eth_drv_remove, + .suspend = netx_eth_drv_suspend, + .resume = netx_eth_drv_resume, + .driver = { + .name = CARDNAME, + .owner = THIS_MODULE, + }, +}; + +static int __init netx_eth_init(void) +{ + unsigned int phy_control, val; + + printk("NetX Ethernet driver\n"); + + phy_control = PHY_CONTROL_PHY_ADDRESS(INTERNAL_PHY_ADR>>1) | + PHY_CONTROL_PHY1_MODE(PHY_MODE_ALL) | + PHY_CONTROL_PHY1_AUTOMDIX | + PHY_CONTROL_PHY1_EN | + PHY_CONTROL_PHY0_MODE(PHY_MODE_ALL) | + PHY_CONTROL_PHY0_AUTOMDIX | + PHY_CONTROL_PHY0_EN | + PHY_CONTROL_CLK_XLATIN; + + val = readl(NETX_SYSTEM_IOC_ACCESS_KEY); + writel(val, NETX_SYSTEM_IOC_ACCESS_KEY); + + writel(phy_control | PHY_CONTROL_RESET, NETX_SYSTEM_PHY_CONTROL); + udelay(100); + + val = readl(NETX_SYSTEM_IOC_ACCESS_KEY); + writel(val, NETX_SYSTEM_IOC_ACCESS_KEY); + + writel(phy_control, NETX_SYSTEM_PHY_CONTROL); + + return platform_driver_register(&netx_eth_driver); +} + +static void __exit netx_eth_cleanup(void) +{ + platform_driver_unregister(&netx_eth_driver); +} + +module_init(netx_eth_init); +module_exit(netx_eth_cleanup); + +MODULE_AUTHOR("Sascha Hauer, Pengutronix"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c index 71f45056a70..e80d1e3aec6 100644 --- a/drivers/net/pcmcia/xirc2ps_cs.c +++ b/drivers/net/pcmcia/xirc2ps_cs.c @@ -1359,7 +1359,7 @@ do_start_xmit(struct sk_buff *skb, struct net_device *dev) kio_addr_t ioaddr = dev->base_addr; int okay; unsigned freespace; - unsigned pktlen = skb? skb->len : 0; + unsigned pktlen = skb->len; DEBUG(1, "do_start_xmit(skb=%p, dev=%p) len=%u\n", skb, dev, pktlen); diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index cda3e53d691..2ba6d3a40e2 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -44,6 +44,11 @@ config CICADA_PHY depends on PHYLIB ---help--- Currently supports the cis8204 +config VITESSE_PHY + tristate "Drivers for the Vitesse PHYs" + depends on PHYLIB + ---help--- + Currently supports the vsc8244 config SMSC_PHY tristate "Drivers for SMSC PHYs" diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index d9614134cc0..a00e6194252 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_CICADA_PHY) += cicada.o obj-$(CONFIG_LXT_PHY) += lxt.o obj-$(CONFIG_QSEMI_PHY) += qsemi.o obj-$(CONFIG_SMSC_PHY) += smsc.o +obj-$(CONFIG_VITESSE_PHY) += vitesse.o diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c new file mode 100644 index 00000000000..ffd215d9a9b --- /dev/null +++ b/drivers/net/phy/vitesse.c @@ -0,0 +1,112 @@ +/* + * Driver for Vitesse PHYs + * + * Author: Kriston Carson + * + * Copyright (c) 2005 Freescale Semiconductor, Inc. + * + * 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/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mii.h> +#include <linux/ethtool.h> +#include <linux/phy.h> + +/* Vitesse Extended Control Register 1 */ +#define MII_VSC8244_EXT_CON1 0x17 +#define MII_VSC8244_EXTCON1_INIT 0x0000 + +/* Vitesse Interrupt Mask Register */ +#define MII_VSC8244_IMASK 0x19 +#define MII_VSC8244_IMASK_IEN 0x8000 +#define MII_VSC8244_IMASK_SPEED 0x4000 +#define MII_VSC8244_IMASK_LINK 0x2000 +#define MII_VSC8244_IMASK_DUPLEX 0x1000 +#define MII_VSC8244_IMASK_MASK 0xf000 + +/* Vitesse Interrupt Status Register */ +#define MII_VSC8244_ISTAT 0x1a +#define MII_VSC8244_ISTAT_STATUS 0x8000 +#define MII_VSC8244_ISTAT_SPEED 0x4000 +#define MII_VSC8244_ISTAT_LINK 0x2000 +#define MII_VSC8244_ISTAT_DUPLEX 0x1000 + +/* Vitesse Auxiliary Control/Status Register */ +#define MII_VSC8244_AUX_CONSTAT 0x1c +#define MII_VSC8244_AUXCONSTAT_INIT 0x0004 +#define MII_VSC8244_AUXCONSTAT_DUPLEX 0x0020 +#define MII_VSC8244_AUXCONSTAT_SPEED 0x0018 +#define MII_VSC8244_AUXCONSTAT_GBIT 0x0010 +#define MII_VSC8244_AUXCONSTAT_100 0x0008 + +MODULE_DESCRIPTION("Vitesse PHY driver"); +MODULE_AUTHOR("Kriston Carson"); +MODULE_LICENSE("GPL"); + +static int vsc824x_config_init(struct phy_device *phydev) +{ + int err; + + err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT, + MII_VSC8244_AUXCONSTAT_INIT); + if (err < 0) + return err; + + err = phy_write(phydev, MII_VSC8244_EXT_CON1, + MII_VSC8244_EXTCON1_INIT); + return err; +} + +static int vsc824x_ack_interrupt(struct phy_device *phydev) +{ + int err = phy_read(phydev, MII_VSC8244_ISTAT); + + return (err < 0) ? err : 0; +} + +static int vsc824x_config_intr(struct phy_device *phydev) +{ + int err; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) + err = phy_write(phydev, MII_VSC8244_IMASK, + MII_VSC8244_IMASK_MASK); + else + err = phy_write(phydev, MII_VSC8244_IMASK, 0); + return err; +} + +/* Vitesse 824x */ +static struct phy_driver vsc8244_driver = { + .phy_id = 0x000fc6c2, + .name = "Vitesse VSC8244", + .phy_id_mask = 0x000fffc0, + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_init = &vsc824x_config_init, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &vsc824x_ack_interrupt, + .config_intr = &vsc824x_config_intr, + .driver = { .owner = THIS_MODULE,}, +}; + +static int __init vsc8244_init(void) +{ + return phy_driver_register(&vsc8244_driver); +} + +static void __exit vsc8244_exit(void) +{ + phy_driver_unregister(&vsc8244_driver); +} + +module_init(vsc8244_init); +module_exit(vsc8244_exit); diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index cac9fdd2e1d..11daed495b9 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -2627,6 +2627,50 @@ no_rx: #endif /** + * s2io_netpoll - Rx interrupt service handler for netpoll support + * @dev : pointer to the device structure. + * Description: + * Polling 'interrupt' - used by things like netconsole to send skbs + * without having to re-enable interrupts. It's not called while + * the interrupt routine is executing. + */ + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void s2io_netpoll(struct net_device *dev) +{ + nic_t *nic = dev->priv; + mac_info_t *mac_control; + struct config_param *config; + XENA_dev_config_t __iomem *bar0 = nic->bar0; + u64 val64; + int i; + + disable_irq(dev->irq); + + atomic_inc(&nic->isr_cnt); + mac_control = &nic->mac_control; + config = &nic->config; + + val64 = readq(&bar0->rx_traffic_int); + writeq(val64, &bar0->rx_traffic_int); + + for (i = 0; i < config->rx_ring_num; i++) + rx_intr_handler(&mac_control->rings[i]); + + for (i = 0; i < config->rx_ring_num; i++) { + if (fill_rx_buffers(nic, i) == -ENOMEM) { + DBG_PRINT(ERR_DBG, "%s:Out of memory", dev->name); + DBG_PRINT(ERR_DBG, " in Rx Netpoll!!\n"); + break; + } + } + atomic_dec(&nic->isr_cnt); + enable_irq(dev->irq); + return; +} +#endif + +/** * rx_intr_handler - Rx interrupt handler * @nic: device private variable. * Description: @@ -6967,6 +7011,10 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) dev->weight = 32; #endif +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = s2io_netpoll; +#endif + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM; if (sp->high_dma_flag == TRUE) dev->features |= NETIF_F_HIGHDMA; diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c index 61eec46cb11..f13b2a195c7 100644 --- a/drivers/net/sundance.c +++ b/drivers/net/sundance.c @@ -94,11 +94,13 @@ Version LK1.10 (Philippe De Muyter phdm@macqel.be): - Make 'unblock interface after Tx underrun' work + Version LK1.11 (Pedro Alejandro Lopez-Valencia palopezv at gmail.com): + - Add support for IC Plus Corporation IP100A chipset */ #define DRV_NAME "sundance" -#define DRV_VERSION "1.01+LK1.10" -#define DRV_RELDATE "28-Oct-2005" +#define DRV_VERSION "1.01+LK1.11" +#define DRV_RELDATE "14-Jun-2006" /* The user-configurable values. @@ -287,6 +289,7 @@ static struct pci_device_id sundance_pci_tbl[] = { {0x1186, 0x1002, 0x1186, 0x1040, 0, 0, 3}, {0x1186, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, {0x13F0, 0x0201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5}, + {0x13F0, 0x0200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6}, {0,} }; MODULE_DEVICE_TABLE(pci, sundance_pci_tbl); @@ -305,6 +308,7 @@ static const struct pci_id_info pci_id_tbl[] = { {"D-Link DFE-530TXS FAST Ethernet Adapter"}, {"D-Link DL10050-based FAST Ethernet Adapter"}, {"Sundance Technology Alta"}, + {"IC Plus Corporation IP100A FAST Ethernet Adapter"}, {NULL,}, /* 0 terminated list. */ }; diff --git a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c index 23032a7bc0a..c3cb8d26cfe 100644 --- a/drivers/net/tokenring/olympic.c +++ b/drivers/net/tokenring/olympic.c @@ -217,7 +217,7 @@ static int __devinit olympic_probe(struct pci_dev *pdev, const struct pci_device dev = alloc_trdev(sizeof(struct olympic_private)) ; if (!dev) { i = -ENOMEM; - goto op_free_dev; + goto op_release_dev; } olympic_priv = dev->priv ; @@ -282,8 +282,8 @@ op_free_iomap: if (olympic_priv->olympic_lap) iounmap(olympic_priv->olympic_lap); -op_free_dev: free_netdev(dev); +op_release_dev: pci_release_regions(pdev); op_disable_dev: diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index 2eb6b5f9ba0..09e05fe40c3 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -248,6 +248,7 @@ static void velocity_free_rd_ring(struct velocity_info *vptr); static void velocity_free_tx_buf(struct velocity_info *vptr, struct velocity_td_info *); static int velocity_soft_reset(struct velocity_info *vptr); static void mii_init(struct velocity_info *vptr, u32 mii_status); +static u32 velocity_get_link(struct net_device *dev); static u32 velocity_get_opt_media_mode(struct velocity_info *vptr); static void velocity_print_link_status(struct velocity_info *vptr); static void safe_disable_mii_autopoll(struct mac_regs __iomem * regs); @@ -798,6 +799,9 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi if (ret < 0) goto err_iounmap; + if (velocity_get_link(dev)) + netif_carrier_off(dev); + velocity_print_info(vptr); pci_set_drvdata(pdev, dev); @@ -1653,8 +1657,10 @@ static void velocity_error(struct velocity_info *vptr, int status) if (linked) { vptr->mii_status &= ~VELOCITY_LINK_FAIL; + netif_carrier_on(vptr->dev); } else { vptr->mii_status |= VELOCITY_LINK_FAIL; + netif_carrier_off(vptr->dev); } velocity_print_link_status(vptr); diff --git a/drivers/net/wan/c101.c b/drivers/net/wan/c101.c index 43d854ace23..b60ef02db7b 100644 --- a/drivers/net/wan/c101.c +++ b/drivers/net/wan/c101.c @@ -326,21 +326,21 @@ static int __init c101_run(unsigned long irq, unsigned long winbase) if (request_irq(irq, sca_intr, 0, devname, card)) { printk(KERN_ERR "c101: could not allocate IRQ\n"); c101_destroy_card(card); - return(-EBUSY); + return -EBUSY; } card->irq = irq; if (!request_mem_region(winbase, C101_MAPPED_RAM_SIZE, devname)) { printk(KERN_ERR "c101: could not request RAM window\n"); c101_destroy_card(card); - return(-EBUSY); + return -EBUSY; } card->phy_winbase = winbase; card->win0base = ioremap(winbase, C101_MAPPED_RAM_SIZE); if (!card->win0base) { printk(KERN_ERR "c101: could not map I/O address\n"); c101_destroy_card(card); - return -EBUSY; + return -EFAULT; } card->tx_ring_buffers = TX_RING_BUFFERS; diff --git a/drivers/net/wan/hdlc_generic.c b/drivers/net/wan/hdlc_generic.c index 46cef8f9213..57f9538b8fb 100644 --- a/drivers/net/wan/hdlc_generic.c +++ b/drivers/net/wan/hdlc_generic.c @@ -259,7 +259,7 @@ int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) } } -static void hdlc_setup(struct net_device *dev) +void hdlc_setup(struct net_device *dev) { hdlc_device *hdlc = dev_to_hdlc(dev); @@ -288,26 +288,6 @@ struct net_device *alloc_hdlcdev(void *priv) return dev; } -int register_hdlc_device(struct net_device *dev) -{ - int result = dev_alloc_name(dev, "hdlc%d"); - if (result < 0) - return result; - - result = register_netdev(dev); - if (result != 0) - return -EIO; - -#if 0 - if (netif_carrier_ok(dev)) - netif_carrier_off(dev); /* no carrier until DCD goes up */ -#endif - - return 0; -} - - - void unregister_hdlc_device(struct net_device *dev) { rtnl_lock(); @@ -326,8 +306,8 @@ EXPORT_SYMBOL(hdlc_open); EXPORT_SYMBOL(hdlc_close); EXPORT_SYMBOL(hdlc_set_carrier); EXPORT_SYMBOL(hdlc_ioctl); +EXPORT_SYMBOL(hdlc_setup); EXPORT_SYMBOL(alloc_hdlcdev); -EXPORT_SYMBOL(register_hdlc_device); EXPORT_SYMBOL(unregister_hdlc_device); static struct packet_type hdlc_packet_type = { diff --git a/drivers/net/wan/n2.c b/drivers/net/wan/n2.c index cd32751b64e..b7d88db89a5 100644 --- a/drivers/net/wan/n2.c +++ b/drivers/net/wan/n2.c @@ -387,6 +387,11 @@ static int __init n2_run(unsigned long io, unsigned long irq, } card->phy_winbase = winbase; card->winbase = ioremap(winbase, USE_WINDOWSIZE); + if (!card->winbase) { + printk(KERN_ERR "n2: ioremap() failed\n"); + n2_destroy_card(card); + return -EFAULT; + } outb(0, io + N2_PCR); outb(winbase >> 12, io + N2_BAR); diff --git a/drivers/net/wan/pci200syn.c b/drivers/net/wan/pci200syn.c index f485a97844c..670e8bd2245 100644 --- a/drivers/net/wan/pci200syn.c +++ b/drivers/net/wan/pci200syn.c @@ -354,6 +354,7 @@ static int __devinit pci200_pci_init_one(struct pci_dev *pdev, card->rambase == NULL) { printk(KERN_ERR "pci200syn: ioremap() failed\n"); pci200_pci_remove_one(pdev); + return -EFAULT; } /* Reset PLX */ diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c index 29a756dd979..437e0e938e3 100644 --- a/drivers/net/wan/wanxl.c +++ b/drivers/net/wan/wanxl.c @@ -634,7 +634,13 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev, /* set up PLX mapping */ plx_phy = pci_resource_start(pdev, 0); + card->plx = ioremap_nocache(plx_phy, 0x70); + if (!card->plx) { + printk(KERN_ERR "wanxl: ioremap() failed\n"); + wanxl_pci_remove_one(pdev); + return -EFAULT; + } #if RESET_WHILE_LOADING wanxl_reset(card); @@ -700,6 +706,12 @@ static int __devinit wanxl_pci_init_one(struct pci_dev *pdev, } mem = ioremap_nocache(mem_phy, PDM_OFFSET + sizeof(firmware)); + if (!mem) { + printk(KERN_ERR "wanxl: ioremap() failed\n"); + wanxl_pci_remove_one(pdev); + return -EFAULT; + } + for (i = 0; i < sizeof(firmware); i += 4) writel(htonl(*(u32*)(firmware + i)), mem + PDM_OFFSET + i); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h index e66fdb1f3cf..d8f917c21ea 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx.h @@ -636,6 +636,17 @@ struct bcm43xx_key { u8 algorithm; }; +/* Driver initialization status. */ +enum { + BCM43xx_STAT_UNINIT, /* Uninitialized. */ + BCM43xx_STAT_INITIALIZING, /* init_board() in progress. */ + BCM43xx_STAT_INITIALIZED, /* Fully operational. */ + BCM43xx_STAT_SHUTTINGDOWN, /* free_board() in progress. */ + BCM43xx_STAT_RESTARTING, /* controller_restart() called. */ +}; +#define bcm43xx_status(bcm) atomic_read(&(bcm)->init_status) +#define bcm43xx_set_status(bcm, stat) atomic_set(&(bcm)->init_status, (stat)) + struct bcm43xx_private { struct ieee80211_device *ieee; struct ieee80211softmac_device *softmac; @@ -646,18 +657,17 @@ struct bcm43xx_private { void __iomem *mmio_addr; - /* Do not use the lock directly. Use the bcm43xx_lock* helper - * functions, to be MMIO-safe. */ - spinlock_t _lock; + /* Locking, see "theory of locking" text below. */ + spinlock_t irq_lock; + struct mutex mutex; - /* Driver status flags. */ - u32 initialized:1, /* init_board() succeed */ - was_initialized:1, /* for PCI suspend/resume. */ - shutting_down:1, /* free_board() in progress */ + /* Driver initialization status BCM43xx_STAT_*** */ + atomic_t init_status; + + u16 was_initialized:1, /* for PCI suspend/resume. */ __using_pio:1, /* Internal, use bcm43xx_using_pio(). */ bad_frames_preempt:1, /* Use "Bad Frames Preemption" (default off) */ reg124_set_0x4:1, /* Some variable to keep track of IRQ stuff. */ - powersaving:1, /* TRUE if we are in PowerSaving mode. FALSE otherwise. */ short_preamble:1, /* TRUE, if short preamble is enabled. */ firmware_norelease:1; /* Do not release the firmware. Used on suspend. */ @@ -721,7 +731,7 @@ struct bcm43xx_private { struct tasklet_struct isr_tasklet; /* Periodic tasks */ - struct timer_list periodic_tasks; + struct work_struct periodic_work; unsigned int periodic_state; struct work_struct restart_work; @@ -746,21 +756,55 @@ struct bcm43xx_private { #endif }; -/* bcm43xx_(un)lock() protect struct bcm43xx_private. - * Note that _NO_ MMIO writes are allowed. If you want to - * write to the device through MMIO in the critical section, use - * the *_mmio lock functions. - * MMIO read-access is allowed, though. - */ -#define bcm43xx_lock(bcm, flags) spin_lock_irqsave(&(bcm)->_lock, flags) -#define bcm43xx_unlock(bcm, flags) spin_unlock_irqrestore(&(bcm)->_lock, flags) -/* bcm43xx_(un)lock_mmio() protect struct bcm43xx_private and MMIO. - * MMIO write-access to the device is allowed. - * All MMIO writes are flushed on unlock, so it is guaranteed to not - * interfere with other threads writing MMIO registers. + +/* *** THEORY OF LOCKING *** + * + * We have two different locks in the bcm43xx driver. + * => bcm->mutex: General sleeping mutex. Protects struct bcm43xx_private + * and the device registers. + * => bcm->irq_lock: IRQ spinlock. Protects against IRQ handler concurrency. + * + * We have three types of helper function pairs to utilize these locks. + * (Always use the helper functions.) + * 1) bcm43xx_{un}lock_noirq(): + * Takes bcm->mutex. Does _not_ protect against IRQ concurrency, + * so it is almost always unsafe, if device IRQs are enabled. + * So only use this, if device IRQs are masked. + * Locking may sleep. + * You can sleep within the critical section. + * 2) bcm43xx_{un}lock_irqonly(): + * Takes bcm->irq_lock. Does _not_ protect against + * bcm43xx_lock_noirq() critical sections. + * Does only protect against the IRQ handler path and other + * irqonly() critical sections. + * Locking does not sleep. + * You must not sleep within the critical section. + * 3) bcm43xx_{un}lock_irqsafe(): + * This is the cummulative lock and takes both, mutex and irq_lock. + * Protects against noirq() and irqonly() critical sections (and + * the IRQ handler path). + * Locking may sleep. + * You must not sleep within the critical section. */ -#define bcm43xx_lock_mmio(bcm, flags) bcm43xx_lock(bcm, flags) -#define bcm43xx_unlock_mmio(bcm, flags) do { mmiowb(); bcm43xx_unlock(bcm, flags); } while (0) + +/* Lock type 1 */ +#define bcm43xx_lock_noirq(bcm) mutex_lock(&(bcm)->mutex) +#define bcm43xx_unlock_noirq(bcm) mutex_unlock(&(bcm)->mutex) +/* Lock type 2 */ +#define bcm43xx_lock_irqonly(bcm, flags) \ + spin_lock_irqsave(&(bcm)->irq_lock, flags) +#define bcm43xx_unlock_irqonly(bcm, flags) \ + spin_unlock_irqrestore(&(bcm)->irq_lock, flags) +/* Lock type 3 */ +#define bcm43xx_lock_irqsafe(bcm, flags) do { \ + bcm43xx_lock_noirq(bcm); \ + bcm43xx_lock_irqonly(bcm, flags); \ + } while (0) +#define bcm43xx_unlock_irqsafe(bcm, flags) do { \ + bcm43xx_unlock_irqonly(bcm, flags); \ + bcm43xx_unlock_noirq(bcm); \ + } while (0) + static inline struct bcm43xx_private * bcm43xx_priv(struct net_device *dev) @@ -843,16 +887,6 @@ struct bcm43xx_radioinfo * bcm43xx_current_radio(struct bcm43xx_private *bcm) return &(bcm->core_80211_ext[bcm->current_80211_core_idx].radio); } -/* Are we running in init_board() context? */ -static inline -int bcm43xx_is_initializing(struct bcm43xx_private *bcm) -{ - if (bcm->initialized) - return 0; - if (bcm->shutting_down) - return 0; - return 1; -} static inline struct bcm43xx_lopair * bcm43xx_get_lopair(struct bcm43xx_phyinfo *phy, diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c index 7497fb16076..ce2e40b29b4 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c @@ -77,8 +77,8 @@ static ssize_t devinfo_read_file(struct file *file, char __user *userbuf, down(&big_buffer_sem); - bcm43xx_lock_mmio(bcm, flags); - if (!bcm->initialized) { + bcm43xx_lock_irqsafe(bcm, flags); + if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) { fappend("Board not initialized.\n"); goto out; } @@ -121,7 +121,7 @@ static ssize_t devinfo_read_file(struct file *file, char __user *userbuf, fappend("\n"); out: - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); up(&big_buffer_sem); return res; @@ -159,8 +159,8 @@ static ssize_t spromdump_read_file(struct file *file, char __user *userbuf, unsigned long flags; down(&big_buffer_sem); - bcm43xx_lock_mmio(bcm, flags); - if (!bcm->initialized) { + bcm43xx_lock_irqsafe(bcm, flags); + if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) { fappend("Board not initialized.\n"); goto out; } @@ -169,7 +169,7 @@ static ssize_t spromdump_read_file(struct file *file, char __user *userbuf, fappend("boardflags: 0x%04x\n", bcm->sprom.boardflags); out: - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); up(&big_buffer_sem); return res; @@ -188,8 +188,8 @@ static ssize_t tsf_read_file(struct file *file, char __user *userbuf, u64 tsf; down(&big_buffer_sem); - bcm43xx_lock_mmio(bcm, flags); - if (!bcm->initialized) { + bcm43xx_lock_irqsafe(bcm, flags); + if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) { fappend("Board not initialized.\n"); goto out; } @@ -199,7 +199,7 @@ static ssize_t tsf_read_file(struct file *file, char __user *userbuf, (unsigned int)(tsf & 0xFFFFFFFFULL)); out: - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); up(&big_buffer_sem); return res; @@ -221,8 +221,8 @@ static ssize_t tsf_write_file(struct file *file, const char __user *user_buf, res = -EFAULT; goto out_up; } - bcm43xx_lock_mmio(bcm, flags); - if (!bcm->initialized) { + bcm43xx_lock_irqsafe(bcm, flags); + if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) { printk(KERN_INFO PFX "debugfs: Board not initialized.\n"); res = -EFAULT; goto out_unlock; @@ -233,10 +233,11 @@ static ssize_t tsf_write_file(struct file *file, const char __user *user_buf, goto out_unlock; } bcm43xx_tsf_write(bcm, tsf); + mmiowb(); res = buf_size; out_unlock: - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); out_up: up(&big_buffer_sem); return res; @@ -257,7 +258,7 @@ static ssize_t txstat_read_file(struct file *file, char __user *userbuf, int i, cnt, j = 0; down(&big_buffer_sem); - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); fappend("Last %d logged xmitstatus blobs (Latest first):\n\n", BCM43xx_NR_LOGGED_XMITSTATUS); @@ -293,14 +294,14 @@ static ssize_t txstat_read_file(struct file *file, char __user *userbuf, i = BCM43xx_NR_LOGGED_XMITSTATUS - 1; } - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); if (*ppos == pos) { /* Done. Drop the copied data. */ e->xmitstatus_printing = 0; } - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); up(&big_buffer_sem); return res; } diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_leds.c b/drivers/net/wireless/bcm43xx/bcm43xx_leds.c index 4b2c02c0b31..ec80692d638 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_leds.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_leds.c @@ -51,12 +51,12 @@ static void bcm43xx_led_blink(unsigned long d) struct bcm43xx_private *bcm = led->bcm; unsigned long flags; - bcm43xx_lock_mmio(bcm, flags); + bcm43xx_lock_irqonly(bcm, flags); if (led->blink_interval) { bcm43xx_led_changestate(led); mod_timer(&led->blink_timer, jiffies + led->blink_interval); } - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqonly(bcm, flags); } static void bcm43xx_led_blink_start(struct bcm43xx_led *led, diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c index 736dde96c4a..085d7857fe3 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -498,20 +498,31 @@ static inline u32 bcm43xx_interrupt_disable(struct bcm43xx_private *bcm, u32 mas return old_mask; } +/* Synchronize IRQ top- and bottom-half. + * IRQs must be masked before calling this. + * This must not be called with the irq_lock held. + */ +static void bcm43xx_synchronize_irq(struct bcm43xx_private *bcm) +{ + synchronize_irq(bcm->irq); + tasklet_disable(&bcm->isr_tasklet); +} + /* Make sure we don't receive more data from the device. */ static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *oldstate) { - u32 old; unsigned long flags; + u32 old; - bcm43xx_lock_mmio(bcm, flags); - if (bcm43xx_is_initializing(bcm) || bcm->shutting_down) { - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_lock_irqonly(bcm, flags); + if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)) { + bcm43xx_unlock_irqonly(bcm, flags); return -EBUSY; } old = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); - tasklet_disable(&bcm->isr_tasklet); - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqonly(bcm, flags); + bcm43xx_synchronize_irq(bcm); + if (oldstate) *oldstate = old; @@ -1389,7 +1400,7 @@ void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy) bcm43xx_dmacontroller_rx_reset(bcm, BCM43xx_MMIO_DMA4_BASE); #endif } - if (bcm->shutting_down) { + if (bcm43xx_status(bcm) == BCM43xx_STAT_SHUTTINGDOWN) { bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) & ~(BCM43xx_SBF_MAC_ENABLED | 0x00000002)); @@ -1709,7 +1720,7 @@ static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm) # define bcmirq_handled(irq) do { /* nothing */ } while (0) #endif /* CONFIG_BCM43XX_DEBUG*/ - bcm43xx_lock_mmio(bcm, flags); + bcm43xx_lock_irqonly(bcm, flags); reason = bcm->irq_reason; dma_reason[0] = bcm->dma_reason[0]; dma_reason[1] = bcm->dma_reason[1]; @@ -1734,7 +1745,8 @@ static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm) dma_reason[0], dma_reason[1], dma_reason[2], dma_reason[3]); bcm43xx_controller_restart(bcm, "DMA error"); - bcm43xx_unlock_mmio(bcm, flags); + mmiowb(); + bcm43xx_unlock_irqonly(bcm, flags); return; } if (unlikely((dma_reason[0] & BCM43xx_DMAIRQ_NONFATALMASK) | @@ -1821,7 +1833,8 @@ static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm) if (!modparam_noleds) bcm43xx_leds_update(bcm, activity); bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate); - bcm43xx_unlock_mmio(bcm, flags); + mmiowb(); + bcm43xx_unlock_irqonly(bcm, flags); } static void pio_irq_workaround(struct bcm43xx_private *bcm, @@ -1870,7 +1883,7 @@ static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id, struct pt_re if (!bcm) return IRQ_NONE; - spin_lock(&bcm->_lock); + spin_lock(&bcm->irq_lock); reason = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); if (reason == 0xffffffff) { @@ -1899,7 +1912,7 @@ static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id, struct pt_re * completely, but some careful work is needed to fix this. I think it * is best to stay with this cheap workaround for now... . */ - if (likely(bcm->initialized)) { + if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) { /* disable all IRQs. They are enabled again in the bottom half. */ bcm->irq_savedstate = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); /* save the reason code and call our bottom half. */ @@ -1909,7 +1922,7 @@ static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id, struct pt_re out: mmiowb(); - spin_unlock(&bcm->_lock); + spin_unlock(&bcm->irq_lock); return ret; } @@ -2133,6 +2146,13 @@ out: return err; } +#ifdef CONFIG_BCM947XX +static struct pci_device_id bcm43xx_47xx_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4324) }, + { 0 } +}; +#endif + static int bcm43xx_initialize_irq(struct bcm43xx_private *bcm) { int res; @@ -2142,11 +2162,15 @@ static int bcm43xx_initialize_irq(struct bcm43xx_private *bcm) bcm->irq = bcm->pci_dev->irq; #ifdef CONFIG_BCM947XX if (bcm->pci_dev->bus->number == 0) { - struct pci_dev *d = NULL; - /* FIXME: we will probably need more device IDs here... */ - d = pci_find_device(PCI_VENDOR_ID_BROADCOM, 0x4324, NULL); - if (d != NULL) { - bcm->irq = d->irq; + struct pci_dev *d; + struct pci_device_id *id; + for (id = bcm43xx_47xx_ids; id->vendor; id++) { + d = pci_get_device(id->vendor, id->device, NULL); + if (d != NULL) { + bcm->irq = d->irq; + pci_dev_put(d); + break; + } } } #endif @@ -3106,15 +3130,10 @@ static void bcm43xx_periodic_every15sec(struct bcm43xx_private *bcm) //TODO for APHY (temperature?) } -static void bcm43xx_periodic_task_handler(unsigned long d) +static void do_periodic_work(struct bcm43xx_private *bcm) { - struct bcm43xx_private *bcm = (struct bcm43xx_private *)d; - unsigned long flags; unsigned int state; - bcm43xx_lock_mmio(bcm, flags); - - assert(bcm->initialized); state = bcm->periodic_state; if (state % 8 == 0) bcm43xx_periodic_every120sec(bcm); @@ -3122,29 +3141,93 @@ static void bcm43xx_periodic_task_handler(unsigned long d) bcm43xx_periodic_every60sec(bcm); if (state % 2 == 0) bcm43xx_periodic_every30sec(bcm); - bcm43xx_periodic_every15sec(bcm); + if (state % 1 == 0) + bcm43xx_periodic_every15sec(bcm); bcm->periodic_state = state + 1; - mod_timer(&bcm->periodic_tasks, jiffies + (HZ * 15)); + schedule_delayed_work(&bcm->periodic_work, HZ * 15); +} + +/* Estimate a "Badness" value based on the periodic work + * state-machine state. "Badness" is worse (bigger), if the + * periodic work will take longer. + */ +static int estimate_periodic_work_badness(unsigned int state) +{ + int badness = 0; + + if (state % 8 == 0) /* every 120 sec */ + badness += 10; + if (state % 4 == 0) /* every 60 sec */ + badness += 5; + if (state % 2 == 0) /* every 30 sec */ + badness += 1; + if (state % 1 == 0) /* every 15 sec */ + badness += 1; - bcm43xx_unlock_mmio(bcm, flags); +#define BADNESS_LIMIT 4 + return badness; +} + +static void bcm43xx_periodic_work_handler(void *d) +{ + struct bcm43xx_private *bcm = d; + unsigned long flags; + u32 savedirqs = 0; + int badness; + + badness = estimate_periodic_work_badness(bcm->periodic_state); + if (badness > BADNESS_LIMIT) { + /* Periodic work will take a long time, so we want it to + * be preemtible. + */ + bcm43xx_lock_irqonly(bcm, flags); + netif_stop_queue(bcm->net_dev); + if (bcm43xx_using_pio(bcm)) + bcm43xx_pio_freeze_txqueues(bcm); + savedirqs = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); + bcm43xx_unlock_irqonly(bcm, flags); + bcm43xx_lock_noirq(bcm); + bcm43xx_synchronize_irq(bcm); + } else { + /* Periodic work should take short time, so we want low + * locking overhead. + */ + bcm43xx_lock_irqsafe(bcm, flags); + } + + do_periodic_work(bcm); + + if (badness > BADNESS_LIMIT) { + bcm43xx_lock_irqonly(bcm, flags); + if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) { + tasklet_enable(&bcm->isr_tasklet); + bcm43xx_interrupt_enable(bcm, savedirqs); + if (bcm43xx_using_pio(bcm)) + bcm43xx_pio_thaw_txqueues(bcm); + } + netif_wake_queue(bcm->net_dev); + mmiowb(); + bcm43xx_unlock_irqonly(bcm, flags); + bcm43xx_unlock_noirq(bcm); + } else { + mmiowb(); + bcm43xx_unlock_irqsafe(bcm, flags); + } } static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm) { - del_timer_sync(&bcm->periodic_tasks); + cancel_rearming_delayed_work(&bcm->periodic_work); } static void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm) { - struct timer_list *timer = &(bcm->periodic_tasks); + struct work_struct *work = &(bcm->periodic_work); - assert(bcm->initialized); - setup_timer(timer, - bcm43xx_periodic_task_handler, - (unsigned long)bcm); - timer->expires = jiffies; - add_timer(timer); + assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED); + INIT_WORK(work, bcm43xx_periodic_work_handler, bcm); + schedule_work(work); } static void bcm43xx_security_init(struct bcm43xx_private *bcm) @@ -3158,16 +3241,12 @@ static void bcm43xx_security_init(struct bcm43xx_private *bcm) static void bcm43xx_free_board(struct bcm43xx_private *bcm) { int i, err; - unsigned long flags; + bcm43xx_lock_noirq(bcm); bcm43xx_sysfs_unregister(bcm); - bcm43xx_periodic_tasks_delete(bcm); - bcm43xx_lock(bcm, flags); - bcm->initialized = 0; - bcm->shutting_down = 1; - bcm43xx_unlock(bcm, flags); + bcm43xx_set_status(bcm, BCM43xx_STAT_SHUTTINGDOWN); for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) { if (!bcm->core_80211[i].available) @@ -3182,23 +3261,19 @@ static void bcm43xx_free_board(struct bcm43xx_private *bcm) bcm43xx_pctl_set_crystal(bcm, 0); - bcm43xx_lock(bcm, flags); - bcm->shutting_down = 0; - bcm43xx_unlock(bcm, flags); + bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT); + bcm43xx_unlock_noirq(bcm); } static int bcm43xx_init_board(struct bcm43xx_private *bcm) { int i, err; int connect_phy; - unsigned long flags; might_sleep(); - bcm43xx_lock(bcm, flags); - bcm->initialized = 0; - bcm->shutting_down = 0; - bcm43xx_unlock(bcm, flags); + bcm43xx_lock_noirq(bcm); + bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZING); err = bcm43xx_pctl_set_crystal(bcm, 1); if (err) @@ -3265,9 +3340,7 @@ static int bcm43xx_init_board(struct bcm43xx_private *bcm) } /* Initialization of the board is done. Flag it as such. */ - bcm43xx_lock(bcm, flags); - bcm->initialized = 1; - bcm43xx_unlock(bcm, flags); + bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZED); bcm43xx_periodic_tasks_setup(bcm); bcm43xx_sysfs_register(bcm); @@ -3278,6 +3351,8 @@ static int bcm43xx_init_board(struct bcm43xx_private *bcm) assert(err == 0); out: + bcm43xx_unlock_noirq(bcm); + return err; err_80211_unwind: @@ -3534,8 +3609,8 @@ static void bcm43xx_ieee80211_set_chan(struct net_device *net_dev, struct bcm43xx_radioinfo *radio; unsigned long flags; - bcm43xx_lock_mmio(bcm, flags); - if (bcm->initialized) { + bcm43xx_lock_irqsafe(bcm, flags); + if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) { bcm43xx_mac_suspend(bcm); bcm43xx_radio_selectchannel(bcm, channel, 0); bcm43xx_mac_enable(bcm); @@ -3543,7 +3618,7 @@ static void bcm43xx_ieee80211_set_chan(struct net_device *net_dev, radio = bcm43xx_current_radio(bcm); radio->initial_channel = channel; } - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); } /* set_security() callback in struct ieee80211_device */ @@ -3557,7 +3632,7 @@ static void bcm43xx_ieee80211_set_security(struct net_device *net_dev, dprintk(KERN_INFO PFX "set security called"); - bcm43xx_lock_mmio(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); for (keyidx = 0; keyidx<WEP_KEYS; keyidx++) if (sec->flags & (1<<keyidx)) { @@ -3587,7 +3662,8 @@ static void bcm43xx_ieee80211_set_security(struct net_device *net_dev, dprintk(", .encrypt = %d", sec->encrypt); } dprintk("\n"); - if (bcm->initialized && !bcm->ieee->host_encrypt) { + if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED && + !bcm->ieee->host_encrypt) { if (secinfo->enabled) { /* upload WEP keys to hardware */ char null_address[6] = { 0 }; @@ -3621,7 +3697,7 @@ static void bcm43xx_ieee80211_set_security(struct net_device *net_dev, } else bcm43xx_clear_keys(bcm); } - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); } /* hard_start_xmit() callback in struct ieee80211_device */ @@ -3633,10 +3709,10 @@ static int bcm43xx_ieee80211_hard_start_xmit(struct ieee80211_txb *txb, int err = -ENODEV; unsigned long flags; - bcm43xx_lock_mmio(bcm, flags); - if (likely(bcm->initialized)) + bcm43xx_lock_irqonly(bcm, flags); + if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) err = bcm43xx_tx(bcm, txb); - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqonly(bcm, flags); return err; } @@ -3651,9 +3727,9 @@ static void bcm43xx_net_tx_timeout(struct net_device *net_dev) struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); unsigned long flags; - bcm43xx_lock_mmio(bcm, flags); + bcm43xx_lock_irqonly(bcm, flags); bcm43xx_controller_restart(bcm, "TX timeout"); - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqonly(bcm, flags); } #ifdef CONFIG_NET_POLL_CONTROLLER @@ -3678,9 +3754,11 @@ static int bcm43xx_net_open(struct net_device *net_dev) static int bcm43xx_net_stop(struct net_device *net_dev) { struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + int err; ieee80211softmac_stop(net_dev); - bcm43xx_disable_interrupts_sync(bcm, NULL); + err = bcm43xx_disable_interrupts_sync(bcm, NULL); + assert(!err); bcm43xx_free_board(bcm); return 0; @@ -3692,6 +3770,7 @@ static int bcm43xx_init_private(struct bcm43xx_private *bcm, { int err; + bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT); bcm->ieee = netdev_priv(net_dev); bcm->softmac = ieee80211_priv(net_dev); bcm->softmac->set_channel = bcm43xx_ieee80211_set_chan; @@ -3700,7 +3779,8 @@ static int bcm43xx_init_private(struct bcm43xx_private *bcm, bcm->pci_dev = pci_dev; bcm->net_dev = net_dev; bcm->bad_frames_preempt = modparam_bad_frames_preempt; - spin_lock_init(&bcm->_lock); + spin_lock_init(&bcm->irq_lock); + mutex_init(&bcm->mutex); tasklet_init(&bcm->isr_tasklet, (void (*)(unsigned long))bcm43xx_interrupt_tasklet, (unsigned long)bcm); @@ -3831,7 +3911,7 @@ static void bcm43xx_chip_reset(void *_bcm) struct net_device *net_dev = bcm->net_dev; struct pci_dev *pci_dev = bcm->pci_dev; int err; - int was_initialized = bcm->initialized; + int was_initialized = (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED); netif_stop_queue(bcm->net_dev); tasklet_disable(&bcm->isr_tasklet); @@ -3866,6 +3946,7 @@ failure: */ void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason) { + bcm43xx_set_status(bcm, BCM43xx_STAT_RESTARTING); bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ printk(KERN_ERR PFX "Controller RESET (%s) ...\n", reason); @@ -3884,11 +3965,11 @@ static int bcm43xx_suspend(struct pci_dev *pdev, pm_message_t state) dprintk(KERN_INFO PFX "Suspending...\n"); - bcm43xx_lock(bcm, flags); - bcm->was_initialized = bcm->initialized; - if (bcm->initialized) + bcm43xx_lock_irqsafe(bcm, flags); + bcm->was_initialized = (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED); + if (bcm->was_initialized) try_to_shutdown = 1; - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); netif_device_detach(net_dev); if (try_to_shutdown) { diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c index b0abac51553..f8200deecc8 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c @@ -1410,7 +1410,10 @@ static inline u16 bcm43xx_phy_lo_g_deviation_subval(struct bcm43xx_private *bcm, u16 control) { struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + u16 ret; + unsigned long flags; + local_irq_save(flags); if (phy->connected) { bcm43xx_phy_write(bcm, 0x15, 0xE300); control <<= 8; @@ -1430,8 +1433,10 @@ u16 bcm43xx_phy_lo_g_deviation_subval(struct bcm43xx_private *bcm, u16 control) bcm43xx_phy_write(bcm, 0x0015, control | 0xFFE0); udelay(8); } + ret = bcm43xx_phy_read(bcm, 0x002D); + local_irq_restore(flags); - return bcm43xx_phy_read(bcm, 0x002D); + return ret; } static u32 bcm43xx_phy_lo_g_singledeviation(struct bcm43xx_private *bcm, u16 control) @@ -1648,7 +1653,7 @@ void bcm43xx_phy_set_baseband_attenuation(struct bcm43xx_private *bcm, void bcm43xx_phy_lo_g_measure(struct bcm43xx_private *bcm) { static const u8 pairorder[10] = { 3, 1, 5, 7, 9, 2, 0, 4, 6, 8 }; - const int is_initializing = bcm43xx_is_initializing(bcm); + const int is_initializing = (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZING); struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); u16 h, i, oldi = 0, j; diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_pio.c b/drivers/net/wireless/bcm43xx/bcm43xx_pio.c index 0aa1bd269a2..574085c4615 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_pio.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_pio.c @@ -262,8 +262,10 @@ static void tx_tasklet(unsigned long d) int err; u16 txctl; - bcm43xx_lock_mmio(bcm, flags); + bcm43xx_lock_irqonly(bcm, flags); + if (queue->tx_frozen) + goto out_unlock; txctl = bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL); if (txctl & BCM43xx_PIO_TXCTL_SUSPEND) goto out_unlock; @@ -298,7 +300,7 @@ static void tx_tasklet(unsigned long d) continue; } out_unlock: - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqonly(bcm, flags); } static void setup_txqueues(struct bcm43xx_pioqueue *queue) @@ -374,7 +376,6 @@ static void cancel_transfers(struct bcm43xx_pioqueue *queue) struct bcm43xx_pio_txpacket *packet, *tmp_packet; netif_tx_disable(queue->bcm->net_dev); - assert(queue->bcm->shutting_down); tasklet_disable(&queue->txtask); list_for_each_entry_safe(packet, tmp_packet, &queue->txrunning, list) @@ -634,5 +635,40 @@ void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue) bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL) & ~BCM43xx_PIO_TXCTL_SUSPEND); bcm43xx_power_saving_ctl_bits(queue->bcm, -1, -1); - tasklet_schedule(&queue->txtask); + if (!list_empty(&queue->txqueue)) + tasklet_schedule(&queue->txtask); +} + +void bcm43xx_pio_freeze_txqueues(struct bcm43xx_private *bcm) +{ + struct bcm43xx_pio *pio; + + assert(bcm43xx_using_pio(bcm)); + pio = bcm43xx_current_pio(bcm); + pio->queue0->tx_frozen = 1; + pio->queue1->tx_frozen = 1; + pio->queue2->tx_frozen = 1; + pio->queue3->tx_frozen = 1; } + +void bcm43xx_pio_thaw_txqueues(struct bcm43xx_private *bcm) +{ + struct bcm43xx_pio *pio; + + assert(bcm43xx_using_pio(bcm)); + pio = bcm43xx_current_pio(bcm); + pio->queue0->tx_frozen = 0; + pio->queue1->tx_frozen = 0; + pio->queue2->tx_frozen = 0; + pio->queue3->tx_frozen = 0; + if (!list_empty(&pio->queue0->txqueue)) + tasklet_schedule(&pio->queue0->txtask); + if (!list_empty(&pio->queue1->txqueue)) + tasklet_schedule(&pio->queue1->txtask); + if (!list_empty(&pio->queue2->txqueue)) + tasklet_schedule(&pio->queue2->txtask); + if (!list_empty(&pio->queue3->txqueue)) + tasklet_schedule(&pio->queue3->txtask); +} + + diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_pio.h b/drivers/net/wireless/bcm43xx/bcm43xx_pio.h index dfc78209e3a..bc78a3c2caf 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_pio.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_pio.h @@ -54,6 +54,7 @@ struct bcm43xx_pioqueue { u16 mmio_base; u8 tx_suspended:1, + tx_frozen:1, need_workarounds:1; /* Workarounds needed for core.rev < 3 */ /* Adjusted size of the device internal TX buffer. */ @@ -108,8 +109,12 @@ void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm, struct bcm43xx_xmitstatus *status); void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue); +/* Suspend a TX queue on hardware level. */ void bcm43xx_pio_tx_suspend(struct bcm43xx_pioqueue *queue); void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue); +/* Suspend (freeze) the TX tasklet (software level). */ +void bcm43xx_pio_freeze_txqueues(struct bcm43xx_private *bcm); +void bcm43xx_pio_thaw_txqueues(struct bcm43xx_private *bcm); #else /* CONFIG_BCM43XX_PIO */ @@ -145,6 +150,14 @@ static inline void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue) { } +static inline +void bcm43xx_pio_freeze_txqueues(struct bcm43xx_private *bcm) +{ +} +static inline +void bcm43xx_pio_thaw_txqueues(struct bcm43xx_private *bcm) +{ +} #endif /* CONFIG_BCM43XX_PIO */ #endif /* BCM43xx_PIO_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c index b438f48e891..6a23bdc7541 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c @@ -120,12 +120,12 @@ static ssize_t bcm43xx_attr_sprom_show(struct device *dev, GFP_KERNEL); if (!sprom) return -ENOMEM; - bcm43xx_lock_mmio(bcm, flags); - assert(bcm->initialized); + bcm43xx_lock_irqsafe(bcm, flags); err = bcm43xx_sprom_read(bcm, sprom); if (!err) err = sprom2hex(sprom, buf, PAGE_SIZE); - bcm43xx_unlock_mmio(bcm, flags); + mmiowb(); + bcm43xx_unlock_irqsafe(bcm, flags); kfree(sprom); return err; @@ -150,10 +150,10 @@ static ssize_t bcm43xx_attr_sprom_store(struct device *dev, err = hex2sprom(sprom, buf, count); if (err) goto out_kfree; - bcm43xx_lock_mmio(bcm, flags); - assert(bcm->initialized); + bcm43xx_lock_irqsafe(bcm, flags); err = bcm43xx_sprom_write(bcm, sprom); - bcm43xx_unlock_mmio(bcm, flags); + mmiowb(); + bcm43xx_unlock_irqsafe(bcm, flags); out_kfree: kfree(sprom); @@ -170,15 +170,13 @@ static ssize_t bcm43xx_attr_interfmode_show(struct device *dev, char *buf) { struct bcm43xx_private *bcm = dev_to_bcm(dev); - unsigned long flags; int err; ssize_t count = 0; if (!capable(CAP_NET_ADMIN)) return -EPERM; - bcm43xx_lock(bcm, flags); - assert(bcm->initialized); + bcm43xx_lock_noirq(bcm); switch (bcm43xx_current_radio(bcm)->interfmode) { case BCM43xx_RADIO_INTERFMODE_NONE: @@ -195,7 +193,7 @@ static ssize_t bcm43xx_attr_interfmode_show(struct device *dev, } err = 0; - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_noirq(bcm); return err ? err : count; @@ -231,16 +229,15 @@ static ssize_t bcm43xx_attr_interfmode_store(struct device *dev, return -EINVAL; } - bcm43xx_lock_mmio(bcm, flags); - assert(bcm->initialized); + bcm43xx_lock_irqsafe(bcm, flags); err = bcm43xx_radio_set_interference_mitigation(bcm, mode); if (err) { printk(KERN_ERR PFX "Interference Mitigation not " "supported by device\n"); } - - bcm43xx_unlock_mmio(bcm, flags); + mmiowb(); + bcm43xx_unlock_irqsafe(bcm, flags); return err ? err : count; } @@ -254,15 +251,13 @@ static ssize_t bcm43xx_attr_preamble_show(struct device *dev, char *buf) { struct bcm43xx_private *bcm = dev_to_bcm(dev); - unsigned long flags; int err; ssize_t count; if (!capable(CAP_NET_ADMIN)) return -EPERM; - bcm43xx_lock(bcm, flags); - assert(bcm->initialized); + bcm43xx_lock_noirq(bcm); if (bcm->short_preamble) count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble enabled)\n"); @@ -270,7 +265,7 @@ static ssize_t bcm43xx_attr_preamble_show(struct device *dev, count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble disabled)\n"); err = 0; - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_noirq(bcm); return err ? err : count; } @@ -290,13 +285,12 @@ static ssize_t bcm43xx_attr_preamble_store(struct device *dev, value = get_boolean(buf, count); if (value < 0) return value; - bcm43xx_lock(bcm, flags); - assert(bcm->initialized); + bcm43xx_lock_irqsafe(bcm, flags); bcm->short_preamble = !!value; err = 0; - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return err ? err : count; } @@ -310,7 +304,7 @@ int bcm43xx_sysfs_register(struct bcm43xx_private *bcm) struct device *dev = &bcm->pci_dev->dev; int err; - assert(bcm->initialized); + assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED); err = device_create_file(dev, &dev_attr_sprom); if (err) diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c index b45063974ae..c35cb3a0777 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c @@ -55,13 +55,13 @@ static int bcm43xx_wx_get_name(struct net_device *net_dev, char *extra) { struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); - unsigned long flags; int i; + unsigned long flags; struct bcm43xx_phyinfo *phy; char suffix[7] = { 0 }; int have_a = 0, have_b = 0, have_g = 0; - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); for (i = 0; i < bcm->nr_80211_available; i++) { phy = &(bcm->core_80211_ext[i].phy); switch (phy->type) { @@ -77,7 +77,7 @@ static int bcm43xx_wx_get_name(struct net_device *net_dev, assert(0); } } - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); i = 0; if (have_a) { @@ -111,7 +111,7 @@ static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev, int freq; int err = -EINVAL; - bcm43xx_lock_mmio(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); if ((data->freq.m >= 0) && (data->freq.m <= 1000)) { channel = data->freq.m; freq = bcm43xx_channel_to_freq(bcm, channel); @@ -121,7 +121,7 @@ static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev, } if (!bcm43xx_is_valid_channel(bcm, channel)) goto out_unlock; - if (bcm->initialized) { + if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) { //ieee80211softmac_disassoc(softmac, $REASON); bcm43xx_mac_suspend(bcm); err = bcm43xx_radio_selectchannel(bcm, channel, 0); @@ -131,7 +131,7 @@ static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev, err = 0; } out_unlock: - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return err; } @@ -147,11 +147,10 @@ static int bcm43xx_wx_get_channelfreq(struct net_device *net_dev, int err = -ENODEV; u16 channel; - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); radio = bcm43xx_current_radio(bcm); channel = radio->channel; if (channel == 0xFF) { - assert(!bcm->initialized); channel = radio->initial_channel; if (channel == 0xFF) goto out_unlock; @@ -163,7 +162,7 @@ static int bcm43xx_wx_get_channelfreq(struct net_device *net_dev, err = 0; out_unlock: - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return err; } @@ -181,13 +180,13 @@ static int bcm43xx_wx_set_mode(struct net_device *net_dev, if (mode == IW_MODE_AUTO) mode = BCM43xx_INITIAL_IWMODE; - bcm43xx_lock_mmio(bcm, flags); - if (bcm->initialized) { + bcm43xx_lock_irqsafe(bcm, flags); + if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) { if (bcm->ieee->iw_mode != mode) bcm43xx_set_iwmode(bcm, mode); } else bcm->ieee->iw_mode = mode; - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return 0; } @@ -200,9 +199,9 @@ static int bcm43xx_wx_get_mode(struct net_device *net_dev, struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); unsigned long flags; - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); data->mode = bcm->ieee->iw_mode; - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return 0; } @@ -255,7 +254,7 @@ static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev, IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); phy = bcm43xx_current_phy(bcm); range->num_bitrates = 0; @@ -302,7 +301,7 @@ static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev, } range->num_frequency = j; - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return 0; } @@ -313,14 +312,13 @@ static int bcm43xx_wx_set_nick(struct net_device *net_dev, char *extra) { struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); - unsigned long flags; size_t len; - bcm43xx_lock(bcm, flags); + bcm43xx_lock_noirq(bcm); len = min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE); memcpy(bcm->nick, extra, len); bcm->nick[len] = '\0'; - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_noirq(bcm); return 0; } @@ -331,15 +329,14 @@ static int bcm43xx_wx_get_nick(struct net_device *net_dev, char *extra) { struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); - unsigned long flags; size_t len; - bcm43xx_lock(bcm, flags); + bcm43xx_lock_noirq(bcm); len = strlen(bcm->nick) + 1; memcpy(extra, bcm->nick, len); data->data.length = (__u16)len; data->data.flags = 1; - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_noirq(bcm); return 0; } @@ -353,7 +350,7 @@ static int bcm43xx_wx_set_rts(struct net_device *net_dev, unsigned long flags; int err = -EINVAL; - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); if (data->rts.disabled) { bcm->rts_threshold = BCM43xx_MAX_RTS_THRESHOLD; err = 0; @@ -364,7 +361,7 @@ static int bcm43xx_wx_set_rts(struct net_device *net_dev, err = 0; } } - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return err; } @@ -377,11 +374,11 @@ static int bcm43xx_wx_get_rts(struct net_device *net_dev, struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); unsigned long flags; - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); data->rts.value = bcm->rts_threshold; data->rts.fixed = 0; data->rts.disabled = (bcm->rts_threshold == BCM43xx_MAX_RTS_THRESHOLD); - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return 0; } @@ -395,7 +392,7 @@ static int bcm43xx_wx_set_frag(struct net_device *net_dev, unsigned long flags; int err = -EINVAL; - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); if (data->frag.disabled) { bcm->ieee->fts = MAX_FRAG_THRESHOLD; err = 0; @@ -406,7 +403,7 @@ static int bcm43xx_wx_set_frag(struct net_device *net_dev, err = 0; } } - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return err; } @@ -419,11 +416,11 @@ static int bcm43xx_wx_get_frag(struct net_device *net_dev, struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); unsigned long flags; - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); data->frag.value = bcm->ieee->fts; data->frag.fixed = 0; data->frag.disabled = (bcm->ieee->fts == MAX_FRAG_THRESHOLD); - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return 0; } @@ -445,8 +442,8 @@ static int bcm43xx_wx_set_xmitpower(struct net_device *net_dev, return -EOPNOTSUPP; } - bcm43xx_lock_mmio(bcm, flags); - if (!bcm->initialized) + bcm43xx_lock_irqsafe(bcm, flags); + if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) goto out_unlock; radio = bcm43xx_current_radio(bcm); phy = bcm43xx_current_phy(bcm); @@ -469,7 +466,7 @@ static int bcm43xx_wx_set_xmitpower(struct net_device *net_dev, err = 0; out_unlock: - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return err; } @@ -484,8 +481,8 @@ static int bcm43xx_wx_get_xmitpower(struct net_device *net_dev, unsigned long flags; int err = -ENODEV; - bcm43xx_lock(bcm, flags); - if (!bcm->initialized) + bcm43xx_lock_irqsafe(bcm, flags); + if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED) goto out_unlock; radio = bcm43xx_current_radio(bcm); /* desired dBm value is in Q5.2 */ @@ -496,7 +493,7 @@ static int bcm43xx_wx_get_xmitpower(struct net_device *net_dev, err = 0; out_unlock: - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return err; } @@ -583,8 +580,8 @@ static int bcm43xx_wx_set_interfmode(struct net_device *net_dev, return -EINVAL; } - bcm43xx_lock_mmio(bcm, flags); - if (bcm->initialized) { + bcm43xx_lock_irqsafe(bcm, flags); + if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) { err = bcm43xx_radio_set_interference_mitigation(bcm, mode); if (err) { printk(KERN_ERR PFX "Interference Mitigation not " @@ -598,7 +595,7 @@ static int bcm43xx_wx_set_interfmode(struct net_device *net_dev, } else bcm43xx_current_radio(bcm)->interfmode = mode; } - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return err; } @@ -612,9 +609,9 @@ static int bcm43xx_wx_get_interfmode(struct net_device *net_dev, unsigned long flags; int mode; - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); mode = bcm43xx_current_radio(bcm)->interfmode; - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); switch (mode) { case BCM43xx_RADIO_INTERFMODE_NONE: @@ -644,9 +641,9 @@ static int bcm43xx_wx_set_shortpreamble(struct net_device *net_dev, int on; on = *((int *)extra); - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); bcm->short_preamble = !!on; - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return 0; } @@ -660,9 +657,9 @@ static int bcm43xx_wx_get_shortpreamble(struct net_device *net_dev, unsigned long flags; int on; - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); on = bcm->short_preamble; - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); if (on) strncpy(extra, "1 (Short Preamble enabled)", MAX_WX_STRING); @@ -684,11 +681,11 @@ static int bcm43xx_wx_set_swencryption(struct net_device *net_dev, on = *((int *)extra); - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); bcm->ieee->host_encrypt = !!on; bcm->ieee->host_decrypt = !!on; bcm->ieee->host_build_iv = !on; - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); return 0; } @@ -702,9 +699,9 @@ static int bcm43xx_wx_get_swencryption(struct net_device *net_dev, unsigned long flags; int on; - bcm43xx_lock(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); on = bcm->ieee->host_encrypt; - bcm43xx_unlock(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); if (on) strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING); @@ -767,11 +764,11 @@ static int bcm43xx_wx_sprom_read(struct net_device *net_dev, if (!sprom) goto out; - bcm43xx_lock_mmio(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); err = -ENODEV; - if (bcm->initialized) + if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) err = bcm43xx_sprom_read(bcm, sprom); - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); if (!err) data->data.length = sprom2hex(sprom, extra); kfree(sprom); @@ -812,11 +809,11 @@ static int bcm43xx_wx_sprom_write(struct net_device *net_dev, if (err) goto out_kfree; - bcm43xx_lock_mmio(bcm, flags); + bcm43xx_lock_irqsafe(bcm, flags); err = -ENODEV; - if (bcm->initialized) + if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) err = bcm43xx_sprom_write(bcm, sprom); - bcm43xx_unlock_mmio(bcm, flags); + bcm43xx_unlock_irqsafe(bcm, flags); out_kfree: kfree(sprom); out: diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c index 39f82f21974..081a8999666 100644 --- a/drivers/net/wireless/ipw2200.c +++ b/drivers/net/wireless/ipw2200.c @@ -533,7 +533,7 @@ static inline void ipw_clear_bit(struct ipw_priv *priv, u32 reg, u32 mask) ipw_write32(priv, reg, ipw_read32(priv, reg) & ~mask); } -static inline void ipw_enable_interrupts(struct ipw_priv *priv) +static inline void __ipw_enable_interrupts(struct ipw_priv *priv) { if (priv->status & STATUS_INT_ENABLED) return; @@ -541,7 +541,7 @@ static inline void ipw_enable_interrupts(struct ipw_priv *priv) ipw_write32(priv, IPW_INTA_MASK_R, IPW_INTA_MASK_ALL); } -static inline void ipw_disable_interrupts(struct ipw_priv *priv) +static inline void __ipw_disable_interrupts(struct ipw_priv *priv) { if (!(priv->status & STATUS_INT_ENABLED)) return; @@ -549,6 +549,24 @@ static inline void ipw_disable_interrupts(struct ipw_priv *priv) ipw_write32(priv, IPW_INTA_MASK_R, ~IPW_INTA_MASK_ALL); } +static inline void ipw_enable_interrupts(struct ipw_priv *priv) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->irq_lock, flags); + __ipw_enable_interrupts(priv); + spin_unlock_irqrestore(&priv->irq_lock, flags); +} + +static inline void ipw_disable_interrupts(struct ipw_priv *priv) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->irq_lock, flags); + __ipw_disable_interrupts(priv); + spin_unlock_irqrestore(&priv->irq_lock, flags); +} + #ifdef CONFIG_IPW2200_DEBUG static char *ipw_error_desc(u32 val) { @@ -1856,7 +1874,7 @@ static void ipw_irq_tasklet(struct ipw_priv *priv) unsigned long flags; int rc = 0; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->irq_lock, flags); inta = ipw_read32(priv, IPW_INTA_RW); inta_mask = ipw_read32(priv, IPW_INTA_MASK_R); @@ -1865,6 +1883,10 @@ static void ipw_irq_tasklet(struct ipw_priv *priv) /* Add any cached INTA values that need to be handled */ inta |= priv->isr_inta; + spin_unlock_irqrestore(&priv->irq_lock, flags); + + spin_lock_irqsave(&priv->lock, flags); + /* handle all the justifications for the interrupt */ if (inta & IPW_INTA_BIT_RX_TRANSFER) { ipw_rx(priv); @@ -1993,10 +2015,10 @@ static void ipw_irq_tasklet(struct ipw_priv *priv) IPW_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled); } + spin_unlock_irqrestore(&priv->lock, flags); + /* enable all interrupts */ ipw_enable_interrupts(priv); - - spin_unlock_irqrestore(&priv->lock, flags); } #define IPW_CMD(x) case IPW_CMD_ ## x : return #x @@ -10460,7 +10482,7 @@ static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs) if (!priv) return IRQ_NONE; - spin_lock(&priv->lock); + spin_lock(&priv->irq_lock); if (!(priv->status & STATUS_INT_ENABLED)) { /* Shared IRQ */ @@ -10482,7 +10504,7 @@ static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs) } /* tell the device to stop sending interrupts */ - ipw_disable_interrupts(priv); + __ipw_disable_interrupts(priv); /* ack current interrupts */ inta &= (IPW_INTA_MASK_ALL & inta_mask); @@ -10493,11 +10515,11 @@ static irqreturn_t ipw_isr(int irq, void *data, struct pt_regs *regs) tasklet_schedule(&priv->irq_tasklet); - spin_unlock(&priv->lock); + spin_unlock(&priv->irq_lock); return IRQ_HANDLED; none: - spin_unlock(&priv->lock); + spin_unlock(&priv->irq_lock); return IRQ_NONE; } @@ -11477,6 +11499,7 @@ static int ipw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) #ifdef CONFIG_IPW2200_DEBUG ipw_debug_level = debug; #endif + spin_lock_init(&priv->irq_lock); spin_lock_init(&priv->lock); for (i = 0; i < IPW_IBSS_MAC_HASH_SIZE; i++) INIT_LIST_HEAD(&priv->ibss_mac_hash[i]); diff --git a/drivers/net/wireless/ipw2200.h b/drivers/net/wireless/ipw2200.h index 6044c0be2c8..ea12ad66b8e 100644 --- a/drivers/net/wireless/ipw2200.h +++ b/drivers/net/wireless/ipw2200.h @@ -1173,6 +1173,7 @@ struct ipw_priv { struct ieee80211_device *ieee; spinlock_t lock; + spinlock_t irq_lock; struct mutex mutex; /* basic pci-network driver stuff */ diff --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c index dade4b90357..5b69befdab7 100644 --- a/drivers/net/wireless/wavelan.c +++ b/drivers/net/wireless/wavelan.c @@ -1695,8 +1695,8 @@ static int wv_frequency_list(unsigned long ioaddr, /* I/O port of the card */ /* Look in the table if the frequency is allowed */ if (table[9 - (freq / 16)] & (1 << (freq % 16))) { /* Compute approximate channel number */ - while ((((channel_bands[c] >> 1) - 24) < freq) && - (c < NELS(channel_bands))) + while ((c < NELS(channel_bands)) && + (((channel_bands[c] >> 1) - 24) < freq)) c++; list[i].i = c; /* Set the list index */ @@ -2903,6 +2903,7 @@ static int wavelan_packet_xmit(struct sk_buff *skb, struct net_device * dev) { net_local *lp = (net_local *) dev->priv; unsigned long flags; + char data[ETH_ZLEN]; #ifdef DEBUG_TX_TRACE printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name, @@ -2937,15 +2938,16 @@ static int wavelan_packet_xmit(struct sk_buff *skb, struct net_device * dev) * able to detect collisions, therefore in theory we don't really * need to pad. Jean II */ if (skb->len < ETH_ZLEN) { - skb = skb_padto(skb, ETH_ZLEN); - if (skb == NULL) - return 0; + memset(data, 0, ETH_ZLEN); + memcpy(data, skb->data, skb->len); + /* Write packet on the card */ + if(wv_packet_write(dev, data, ETH_ZLEN)) + return 1; /* We failed */ } - - /* Write packet on the card */ - if(wv_packet_write(dev, skb->data, skb->len)) + else if(wv_packet_write(dev, skb->data, skb->len)) return 1; /* We failed */ + dev_kfree_skb(skb); #ifdef DEBUG_TX_TRACE diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 6707df96893..f2d152b818f 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -26,7 +26,11 @@ obj-$(CONFIG_PPC32) += setup-irq.o obj-$(CONFIG_PPC64) += setup-bus.o obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o obj-$(CONFIG_X86_VISWS) += setup-irq.o -obj-$(CONFIG_PCI_MSI) += msi.o + +msiobj-y := msi.o msi-apic.o +msiobj-$(CONFIG_IA64_GENERIC) += msi-altix.o +msiobj-$(CONFIG_IA64_SGI_SN2) += msi-altix.o +obj-$(CONFIG_PCI_MSI) += $(msiobj-y) # # ACPI Related PCI FW Functions diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index eed67d9e73b..72309268202 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -81,9 +81,9 @@ void __devinit pci_bus_add_device(struct pci_dev *dev) { device_add(&dev->dev); - spin_lock(&pci_bus_lock); + down_write(&pci_bus_sem); list_add_tail(&dev->global_list, &pci_devices); - spin_unlock(&pci_bus_lock); + up_write(&pci_bus_sem); pci_proc_attach_device(dev); pci_create_sysfs_dev_files(dev); @@ -125,10 +125,10 @@ void __devinit pci_bus_add_devices(struct pci_bus *bus) */ if (dev->subordinate) { if (list_empty(&dev->subordinate->node)) { - spin_lock(&pci_bus_lock); + down_write(&pci_bus_sem); list_add_tail(&dev->subordinate->node, &dev->bus->children); - spin_unlock(&pci_bus_lock); + up_write(&pci_bus_sem); } pci_bus_add_devices(dev->subordinate); @@ -168,7 +168,7 @@ void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *), struct list_head *next; bus = top; - spin_lock(&pci_bus_lock); + down_read(&pci_bus_sem); next = top->devices.next; for (;;) { if (next == &bus->devices) { @@ -180,22 +180,19 @@ void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *), continue; } dev = list_entry(next, struct pci_dev, bus_list); - pci_dev_get(dev); if (dev->subordinate) { /* this is a pci-pci bridge, do its devices next */ next = dev->subordinate->devices.next; bus = dev->subordinate; } else next = dev->bus_list.next; - spin_unlock(&pci_bus_lock); - /* Run device routines with the bus unlocked */ + /* Run device routines with the device locked */ + down(&dev->dev.sem); cb(dev, userdata); - - spin_lock(&pci_bus_lock); - pci_dev_put(dev); + up(&dev->dev.sem); } - spin_unlock(&pci_bus_lock); + up_read(&pci_bus_sem); } EXPORT_SYMBOL_GPL(pci_walk_bus); diff --git a/drivers/pci/msi-altix.c b/drivers/pci/msi-altix.c new file mode 100644 index 00000000000..bed4183a5e3 --- /dev/null +++ b/drivers/pci/msi-altix.c @@ -0,0 +1,210 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2006 Silicon Graphics, Inc. All Rights Reserved. + */ + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/cpumask.h> + +#include <asm/sn/addrs.h> +#include <asm/sn/intr.h> +#include <asm/sn/pcibus_provider_defs.h> +#include <asm/sn/pcidev.h> +#include <asm/sn/nodepda.h> + +#include "msi.h" + +struct sn_msi_info { + u64 pci_addr; + struct sn_irq_info *sn_irq_info; +}; + +static struct sn_msi_info *sn_msi_info; + +static void +sn_msi_teardown(unsigned int vector) +{ + nasid_t nasid; + int widget; + struct pci_dev *pdev; + struct pcidev_info *sn_pdev; + struct sn_irq_info *sn_irq_info; + struct pcibus_bussoft *bussoft; + struct sn_pcibus_provider *provider; + + sn_irq_info = sn_msi_info[vector].sn_irq_info; + if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0) + return; + + sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo; + pdev = sn_pdev->pdi_linux_pcidev; + provider = SN_PCIDEV_BUSPROVIDER(pdev); + + (*provider->dma_unmap)(pdev, + sn_msi_info[vector].pci_addr, + PCI_DMA_FROMDEVICE); + sn_msi_info[vector].pci_addr = 0; + + bussoft = SN_PCIDEV_BUSSOFT(pdev); + nasid = NASID_GET(bussoft->bs_base); + widget = (nasid & 1) ? + TIO_SWIN_WIDGETNUM(bussoft->bs_base) : + SWIN_WIDGETNUM(bussoft->bs_base); + + sn_intr_free(nasid, widget, sn_irq_info); + sn_msi_info[vector].sn_irq_info = NULL; + + return; +} + +int +sn_msi_setup(struct pci_dev *pdev, unsigned int vector, + u32 *addr_hi, u32 *addr_lo, u32 *data) +{ + int widget; + int status; + nasid_t nasid; + u64 bus_addr; + struct sn_irq_info *sn_irq_info; + struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(pdev); + struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev); + + if (bussoft == NULL) + return -EINVAL; + + if (provider == NULL || provider->dma_map_consistent == NULL) + return -EINVAL; + + /* + * Set up the vector plumbing. Let the prom (via sn_intr_alloc) + * decide which cpu to direct this msi at by default. + */ + + nasid = NASID_GET(bussoft->bs_base); + widget = (nasid & 1) ? + TIO_SWIN_WIDGETNUM(bussoft->bs_base) : + SWIN_WIDGETNUM(bussoft->bs_base); + + sn_irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL); + if (! sn_irq_info) + return -ENOMEM; + + status = sn_intr_alloc(nasid, widget, sn_irq_info, vector, -1, -1); + if (status) { + kfree(sn_irq_info); + return -ENOMEM; + } + + sn_irq_info->irq_int_bit = -1; /* mark this as an MSI irq */ + sn_irq_fixup(pdev, sn_irq_info); + + /* Prom probably should fill these in, but doesn't ... */ + sn_irq_info->irq_bridge_type = bussoft->bs_asic_type; + sn_irq_info->irq_bridge = (void *)bussoft->bs_base; + + /* + * Map the xio address into bus space + */ + bus_addr = (*provider->dma_map_consistent)(pdev, + sn_irq_info->irq_xtalkaddr, + sizeof(sn_irq_info->irq_xtalkaddr), + SN_DMA_MSI|SN_DMA_ADDR_XIO); + if (! bus_addr) { + sn_intr_free(nasid, widget, sn_irq_info); + kfree(sn_irq_info); + return -ENOMEM; + } + + sn_msi_info[vector].sn_irq_info = sn_irq_info; + sn_msi_info[vector].pci_addr = bus_addr; + + *addr_hi = (u32)(bus_addr >> 32); + *addr_lo = (u32)(bus_addr & 0x00000000ffffffff); + + /* + * In the SN platform, bit 16 is a "send vector" bit which + * must be present in order to move the vector through the system. + */ + *data = 0x100 + (unsigned int)vector; + +#ifdef CONFIG_SMP + set_irq_affinity_info((vector & 0xff), sn_irq_info->irq_cpuid, 0); +#endif + + return 0; +} + +static void +sn_msi_target(unsigned int vector, unsigned int cpu, + u32 *addr_hi, u32 *addr_lo) +{ + int slice; + nasid_t nasid; + u64 bus_addr; + struct pci_dev *pdev; + struct pcidev_info *sn_pdev; + struct sn_irq_info *sn_irq_info; + struct sn_irq_info *new_irq_info; + struct sn_pcibus_provider *provider; + + sn_irq_info = sn_msi_info[vector].sn_irq_info; + if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0) + return; + + /* + * Release XIO resources for the old MSI PCI address + */ + + sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo; + pdev = sn_pdev->pdi_linux_pcidev; + provider = SN_PCIDEV_BUSPROVIDER(pdev); + + bus_addr = (u64)(*addr_hi) << 32 | (u64)(*addr_lo); + (*provider->dma_unmap)(pdev, bus_addr, PCI_DMA_FROMDEVICE); + sn_msi_info[vector].pci_addr = 0; + + nasid = cpuid_to_nasid(cpu); + slice = cpuid_to_slice(cpu); + + new_irq_info = sn_retarget_vector(sn_irq_info, nasid, slice); + sn_msi_info[vector].sn_irq_info = new_irq_info; + if (new_irq_info == NULL) + return; + + /* + * Map the xio address into bus space + */ + + bus_addr = (*provider->dma_map_consistent)(pdev, + new_irq_info->irq_xtalkaddr, + sizeof(new_irq_info->irq_xtalkaddr), + SN_DMA_MSI|SN_DMA_ADDR_XIO); + + sn_msi_info[vector].pci_addr = bus_addr; + *addr_hi = (u32)(bus_addr >> 32); + *addr_lo = (u32)(bus_addr & 0x00000000ffffffff); +} + +struct msi_ops sn_msi_ops = { + .setup = sn_msi_setup, + .teardown = sn_msi_teardown, +#ifdef CONFIG_SMP + .target = sn_msi_target, +#endif +}; + +int +sn_msi_init(void) +{ + sn_msi_info = + kzalloc(sizeof(struct sn_msi_info) * NR_VECTORS, GFP_KERNEL); + if (! sn_msi_info) + return -ENOMEM; + + msi_register(&sn_msi_ops); + return 0; +} diff --git a/drivers/pci/msi-apic.c b/drivers/pci/msi-apic.c new file mode 100644 index 00000000000..0eb5fe9003a --- /dev/null +++ b/drivers/pci/msi-apic.c @@ -0,0 +1,100 @@ +/* + * MSI hooks for standard x86 apic + */ + +#include <linux/pci.h> +#include <linux/irq.h> + +#include "msi.h" + +/* + * Shifts for APIC-based data + */ + +#define MSI_DATA_VECTOR_SHIFT 0 +#define MSI_DATA_VECTOR(v) (((u8)v) << MSI_DATA_VECTOR_SHIFT) + +#define MSI_DATA_DELIVERY_SHIFT 8 +#define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_SHIFT) +#define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_SHIFT) + +#define MSI_DATA_LEVEL_SHIFT 14 +#define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT) +#define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT) + +#define MSI_DATA_TRIGGER_SHIFT 15 +#define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT) +#define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT) + +/* + * Shift/mask fields for APIC-based bus address + */ + +#define MSI_ADDR_HEADER 0xfee00000 + +#define MSI_ADDR_DESTID_MASK 0xfff0000f +#define MSI_ADDR_DESTID_CPU(cpu) ((cpu) << MSI_TARGET_CPU_SHIFT) + +#define MSI_ADDR_DESTMODE_SHIFT 2 +#define MSI_ADDR_DESTMODE_PHYS (0 << MSI_ADDR_DESTMODE_SHIFT) +#define MSI_ADDR_DESTMODE_LOGIC (1 << MSI_ADDR_DESTMODE_SHIFT) + +#define MSI_ADDR_REDIRECTION_SHIFT 3 +#define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT) +#define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT) + + +static void +msi_target_apic(unsigned int vector, + unsigned int dest_cpu, + u32 *address_hi, /* in/out */ + u32 *address_lo) /* in/out */ +{ + u32 addr = *address_lo; + + addr &= MSI_ADDR_DESTID_MASK; + addr |= MSI_ADDR_DESTID_CPU(cpu_physical_id(dest_cpu)); + + *address_lo = addr; +} + +static int +msi_setup_apic(struct pci_dev *pdev, /* unused in generic */ + unsigned int vector, + u32 *address_hi, + u32 *address_lo, + u32 *data) +{ + unsigned long dest_phys_id; + + dest_phys_id = cpu_physical_id(first_cpu(cpu_online_map)); + + *address_hi = 0; + *address_lo = MSI_ADDR_HEADER | + MSI_ADDR_DESTMODE_PHYS | + MSI_ADDR_REDIRECTION_CPU | + MSI_ADDR_DESTID_CPU(dest_phys_id); + + *data = MSI_DATA_TRIGGER_EDGE | + MSI_DATA_LEVEL_ASSERT | + MSI_DATA_DELIVERY_FIXED | + MSI_DATA_VECTOR(vector); + + return 0; +} + +static void +msi_teardown_apic(unsigned int vector) +{ + return; /* no-op */ +} + +/* + * Generic ops used on most IA archs/platforms. Set with msi_register() + */ + +struct msi_ops msi_apic_ops = { + .setup = msi_setup_apic, + .teardown = msi_teardown_apic, + .target = msi_target_apic, +}; diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 9855c4c920b..7f8429284fa 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -23,8 +23,6 @@ #include "pci.h" #include "msi.h" -#define MSI_TARGET_CPU first_cpu(cpu_online_map) - static DEFINE_SPINLOCK(msi_lock); static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL }; static kmem_cache_t* msi_cachep; @@ -37,9 +35,17 @@ static int nr_msix_devices; #ifndef CONFIG_X86_IO_APIC int vector_irq[NR_VECTORS] = { [0 ... NR_VECTORS - 1] = -1}; -u8 irq_vector[NR_IRQ_VECTORS] = { FIRST_DEVICE_VECTOR , 0 }; #endif +static struct msi_ops *msi_ops; + +int +msi_register(struct msi_ops *ops) +{ + msi_ops = ops; + return 0; +} + static void msi_cache_ctor(void *p, kmem_cache_t *cache, unsigned long flags) { memset(p, 0, NR_IRQS * sizeof(struct msi_desc)); @@ -92,7 +98,7 @@ static void msi_set_mask_bit(unsigned int vector, int flag) static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask) { struct msi_desc *entry; - struct msg_address address; + u32 address_hi, address_lo; unsigned int irq = vector; unsigned int dest_cpu = first_cpu(cpu_mask); @@ -108,28 +114,36 @@ static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask) if (!pos) return; + pci_read_config_dword(entry->dev, msi_upper_address_reg(pos), + &address_hi); pci_read_config_dword(entry->dev, msi_lower_address_reg(pos), - &address.lo_address.value); - address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK; - address.lo_address.value |= (cpu_physical_id(dest_cpu) << - MSI_TARGET_CPU_SHIFT); - entry->msi_attrib.current_cpu = cpu_physical_id(dest_cpu); + &address_lo); + + msi_ops->target(vector, dest_cpu, &address_hi, &address_lo); + + pci_write_config_dword(entry->dev, msi_upper_address_reg(pos), + address_hi); pci_write_config_dword(entry->dev, msi_lower_address_reg(pos), - address.lo_address.value); + address_lo); set_native_irq_info(irq, cpu_mask); break; } case PCI_CAP_ID_MSIX: { - int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET; - - address.lo_address.value = readl(entry->mask_base + offset); - address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK; - address.lo_address.value |= (cpu_physical_id(dest_cpu) << - MSI_TARGET_CPU_SHIFT); - entry->msi_attrib.current_cpu = cpu_physical_id(dest_cpu); - writel(address.lo_address.value, entry->mask_base + offset); + int offset_hi = + entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET; + int offset_lo = + entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET; + + address_hi = readl(entry->mask_base + offset_hi); + address_lo = readl(entry->mask_base + offset_lo); + + msi_ops->target(vector, dest_cpu, &address_hi, &address_lo); + + writel(address_hi, entry->mask_base + offset_hi); + writel(address_lo, entry->mask_base + offset_lo); set_native_irq_info(irq, cpu_mask); break; } @@ -251,30 +265,6 @@ static struct hw_interrupt_type msi_irq_wo_maskbit_type = { .set_affinity = set_msi_affinity }; -static void msi_data_init(struct msg_data *msi_data, - unsigned int vector) -{ - memset(msi_data, 0, sizeof(struct msg_data)); - msi_data->vector = (u8)vector; - msi_data->delivery_mode = MSI_DELIVERY_MODE; - msi_data->level = MSI_LEVEL_MODE; - msi_data->trigger = MSI_TRIGGER_MODE; -} - -static void msi_address_init(struct msg_address *msi_address) -{ - unsigned int dest_id; - unsigned long dest_phys_id = cpu_physical_id(MSI_TARGET_CPU); - - memset(msi_address, 0, sizeof(struct msg_address)); - msi_address->hi_address = (u32)0; - dest_id = (MSI_ADDRESS_HEADER << MSI_ADDRESS_HEADER_SHIFT); - msi_address->lo_address.u.dest_mode = MSI_PHYSICAL_MODE; - msi_address->lo_address.u.redirection_hint = MSI_REDIRECTION_HINT_MODE; - msi_address->lo_address.u.dest_id = dest_id; - msi_address->lo_address.value |= (dest_phys_id << MSI_TARGET_CPU_SHIFT); -} - static int msi_free_vector(struct pci_dev* dev, int vector, int reassign); static int assign_msi_vector(void) { @@ -369,13 +359,29 @@ static int msi_init(void) return status; } + status = msi_arch_init(); + if (status < 0) { + pci_msi_enable = 0; + printk(KERN_WARNING + "PCI: MSI arch init failed. MSI disabled.\n"); + return status; + } + + if (! msi_ops) { + printk(KERN_WARNING + "PCI: MSI ops not registered. MSI disabled.\n"); + status = -EINVAL; + return status; + } + + last_alloc_vector = assign_irq_vector(AUTO_ASSIGN); status = msi_cache_init(); if (status < 0) { pci_msi_enable = 0; printk(KERN_WARNING "PCI: MSI cache init failed\n"); return status; } - last_alloc_vector = assign_irq_vector(AUTO_ASSIGN); + if (last_alloc_vector < 0) { pci_msi_enable = 0; printk(KERN_WARNING "PCI: No interrupt vectors available for MSI\n"); @@ -442,9 +448,11 @@ static void enable_msi_mode(struct pci_dev *dev, int pos, int type) /* Set enabled bits to single MSI & enable MSI_enable bit */ msi_enable(control, 1); pci_write_config_word(dev, msi_control_reg(pos), control); + dev->msi_enabled = 1; } else { msix_enable(control); pci_write_config_word(dev, msi_control_reg(pos), control); + dev->msix_enabled = 1; } if (pci_find_capability(dev, PCI_CAP_ID_EXP)) { /* PCI Express Endpoint device detected */ @@ -461,9 +469,11 @@ void disable_msi_mode(struct pci_dev *dev, int pos, int type) /* Set enabled bits to single MSI & enable MSI_enable bit */ msi_disable(control); pci_write_config_word(dev, msi_control_reg(pos), control); + dev->msi_enabled = 0; } else { msix_disable(control); pci_write_config_word(dev, msi_control_reg(pos), control); + dev->msix_enabled = 0; } if (pci_find_capability(dev, PCI_CAP_ID_EXP)) { /* PCI Express Endpoint device detected */ @@ -538,7 +548,6 @@ int pci_save_msi_state(struct pci_dev *dev) pci_read_config_dword(dev, pos + PCI_MSI_DATA_32, &cap[i++]); if (control & PCI_MSI_FLAGS_MASKBIT) pci_read_config_dword(dev, pos + PCI_MSI_MASK_BIT, &cap[i++]); - disable_msi_mode(dev, pos, PCI_CAP_ID_MSI); save_state->cap_nr = PCI_CAP_ID_MSI; pci_add_saved_cap(dev, save_state); return 0; @@ -575,6 +584,8 @@ void pci_restore_msi_state(struct pci_dev *dev) int pci_save_msix_state(struct pci_dev *dev) { int pos; + int temp; + int vector, head, tail = 0; u16 control; struct pci_cap_saved_state *save_state; @@ -582,6 +593,7 @@ int pci_save_msix_state(struct pci_dev *dev) if (pos <= 0 || dev->no_msi) return 0; + /* save the capability */ pci_read_config_word(dev, msi_control_reg(pos), &control); if (!(control & PCI_MSIX_FLAGS_ENABLE)) return 0; @@ -593,7 +605,38 @@ int pci_save_msix_state(struct pci_dev *dev) } *((u16 *)&save_state->data[0]) = control; - disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); + /* save the table */ + temp = dev->irq; + if (msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) { + kfree(save_state); + return -EINVAL; + } + + vector = head = dev->irq; + while (head != tail) { + int j; + void __iomem *base; + struct msi_desc *entry; + + entry = msi_desc[vector]; + base = entry->mask_base; + j = entry->msi_attrib.entry_nr; + + entry->address_lo_save = + readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); + entry->address_hi_save = + readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); + entry->data_save = + readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_DATA_OFFSET); + + tail = msi_desc[vector]->link.tail; + vector = tail; + } + dev->irq = temp; + save_state->cap_nr = PCI_CAP_ID_MSIX; pci_add_saved_cap(dev, save_state); return 0; @@ -606,8 +649,6 @@ void pci_restore_msix_state(struct pci_dev *dev) int vector, head, tail = 0; void __iomem *base; int j; - struct msg_address address; - struct msg_data data; struct msi_desc *entry; int temp; struct pci_cap_saved_state *save_state; @@ -633,20 +674,13 @@ void pci_restore_msix_state(struct pci_dev *dev) base = entry->mask_base; j = entry->msi_attrib.entry_nr; - msi_address_init(&address); - msi_data_init(&data, vector); - - address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK; - address.lo_address.value |= entry->msi_attrib.current_cpu << - MSI_TARGET_CPU_SHIFT; - - writel(address.lo_address.value, + writel(entry->address_lo_save, base + j * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); - writel(address.hi_address, + writel(entry->address_hi_save, base + j * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); - writel(*(u32*)&data, + writel(entry->data_save, base + j * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_DATA_OFFSET); @@ -660,30 +694,32 @@ void pci_restore_msix_state(struct pci_dev *dev) } #endif -static void msi_register_init(struct pci_dev *dev, struct msi_desc *entry) +static int msi_register_init(struct pci_dev *dev, struct msi_desc *entry) { - struct msg_address address; - struct msg_data data; + int status; + u32 address_hi; + u32 address_lo; + u32 data; int pos, vector = dev->irq; u16 control; pos = pci_find_capability(dev, PCI_CAP_ID_MSI); pci_read_config_word(dev, msi_control_reg(pos), &control); + /* Configure MSI capability structure */ - msi_address_init(&address); - msi_data_init(&data, vector); - entry->msi_attrib.current_cpu = ((address.lo_address.u.dest_id >> - MSI_TARGET_CPU_SHIFT) & MSI_TARGET_CPU_MASK); - pci_write_config_dword(dev, msi_lower_address_reg(pos), - address.lo_address.value); + status = msi_ops->setup(dev, vector, &address_hi, &address_lo, &data); + if (status < 0) + return status; + + pci_write_config_dword(dev, msi_lower_address_reg(pos), address_lo); if (is_64bit_address(control)) { pci_write_config_dword(dev, - msi_upper_address_reg(pos), address.hi_address); + msi_upper_address_reg(pos), address_hi); pci_write_config_word(dev, - msi_data_reg(pos, 1), *((u32*)&data)); + msi_data_reg(pos, 1), data); } else pci_write_config_word(dev, - msi_data_reg(pos, 0), *((u32*)&data)); + msi_data_reg(pos, 0), data); if (entry->msi_attrib.maskbit) { unsigned int maskbits, temp; /* All MSIs are unmasked by default, Mask them all */ @@ -697,6 +733,8 @@ static void msi_register_init(struct pci_dev *dev, struct msi_desc *entry) msi_mask_bits_reg(pos, is_64bit_address(control)), maskbits); } + + return 0; } /** @@ -710,6 +748,7 @@ static void msi_register_init(struct pci_dev *dev, struct msi_desc *entry) **/ static int msi_capability_init(struct pci_dev *dev) { + int status; struct msi_desc *entry; int pos, vector; u16 control; @@ -742,7 +781,12 @@ static int msi_capability_init(struct pci_dev *dev) /* Replace with MSI handler */ irq_handler_init(PCI_CAP_ID_MSI, vector, entry->msi_attrib.maskbit); /* Configure MSI capability structure */ - msi_register_init(dev, entry); + status = msi_register_init(dev, entry); + if (status != 0) { + dev->irq = entry->msi_attrib.default_vector; + kmem_cache_free(msi_cachep, entry); + return status; + } attach_msi_entry(entry, vector); /* Set MSI enabled bits */ @@ -765,8 +809,10 @@ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, int nvec) { struct msi_desc *head = NULL, *tail = NULL, *entry = NULL; - struct msg_address address; - struct msg_data data; + u32 address_hi; + u32 address_lo; + u32 data; + int status; int vector, pos, i, j, nr_entries, temp = 0; unsigned long phys_addr; u32 table_offset; @@ -822,18 +868,20 @@ static int msix_capability_init(struct pci_dev *dev, /* Replace with MSI-X handler */ irq_handler_init(PCI_CAP_ID_MSIX, vector, 1); /* Configure MSI-X capability structure */ - msi_address_init(&address); - msi_data_init(&data, vector); - entry->msi_attrib.current_cpu = - ((address.lo_address.u.dest_id >> - MSI_TARGET_CPU_SHIFT) & MSI_TARGET_CPU_MASK); - writel(address.lo_address.value, + status = msi_ops->setup(dev, vector, + &address_hi, + &address_lo, + &data); + if (status < 0) + break; + + writel(address_lo, base + j * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); - writel(address.hi_address, + writel(address_hi, base + j * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); - writel(*(u32*)&data, + writel(data, base + j * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_DATA_OFFSET); attach_msi_entry(entry, vector); @@ -865,6 +913,7 @@ static int msix_capability_init(struct pci_dev *dev, **/ int pci_enable_msi(struct pci_dev* dev) { + struct pci_bus *bus; int pos, temp, status = -EINVAL; u16 control; @@ -874,8 +923,9 @@ int pci_enable_msi(struct pci_dev* dev) if (dev->no_msi) return status; - if (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_MSI) - return -EINVAL; + for (bus = dev->bus; bus; bus = bus->parent) + if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI) + return -EINVAL; temp = dev->irq; @@ -887,23 +937,23 @@ int pci_enable_msi(struct pci_dev* dev) if (!pos) return -EINVAL; - pci_read_config_word(dev, msi_control_reg(pos), &control); - if (control & PCI_MSI_FLAGS_ENABLE) - return 0; /* Already in MSI mode */ - if (!msi_lookup_vector(dev, PCI_CAP_ID_MSI)) { /* Lookup Sucess */ unsigned long flags; + pci_read_config_word(dev, msi_control_reg(pos), &control); + if (control & PCI_MSI_FLAGS_ENABLE) + return 0; /* Already in MSI mode */ spin_lock_irqsave(&msi_lock, flags); if (!vector_irq[dev->irq]) { msi_desc[dev->irq]->msi_attrib.state = 0; vector_irq[dev->irq] = -1; nr_released_vectors--; spin_unlock_irqrestore(&msi_lock, flags); - msi_register_init(dev, msi_desc[dev->irq]); - enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); - return 0; + status = msi_register_init(dev, msi_desc[dev->irq]); + if (status == 0) + enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + return status; } spin_unlock_irqrestore(&msi_lock, flags); dev->irq = temp; @@ -980,6 +1030,8 @@ static int msi_free_vector(struct pci_dev* dev, int vector, int reassign) void __iomem *base; unsigned long flags; + msi_ops->teardown(vector); + spin_lock_irqsave(&msi_lock, flags); entry = msi_desc[vector]; if (!entry || entry->dev != dev) { @@ -1008,33 +1060,8 @@ static int msi_free_vector(struct pci_dev* dev, int vector, int reassign) entry_nr * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); - if (head == vector) { - /* - * Detect last MSI-X vector to be released. - * Release the MSI-X memory-mapped table. - */ -#if 0 - int pos, nr_entries; - unsigned long phys_addr; - u32 table_offset; - u16 control; - u8 bir; - - pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - pci_read_config_word(dev, msi_control_reg(pos), - &control); - nr_entries = multi_msix_capable(control); - pci_read_config_dword(dev, msix_table_offset_reg(pos), - &table_offset); - bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK); - table_offset &= ~PCI_MSIX_FLAGS_BIRMASK; - phys_addr = pci_resource_start(dev, bir) + table_offset; -/* - * FIXME! and what did you want to do with phys_addr? - */ -#endif + if (head == vector) iounmap(base); - } } return 0; @@ -1108,6 +1135,7 @@ static int reroute_msix_table(int head, struct msix_entry *entries, int *nvec) **/ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) { + struct pci_bus *bus; int status, pos, nr_entries, free_vectors; int i, j, temp; u16 control; @@ -1116,6 +1144,13 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) if (!pci_msi_enable || !dev || !entries) return -EINVAL; + if (dev->no_msi) + return -EINVAL; + + for (bus = dev->bus; bus; bus = bus->parent) + if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI) + return -EINVAL; + status = msi_init(); if (status < 0) return status; @@ -1300,24 +1335,6 @@ void msi_remove_pci_irq_vectors(struct pci_dev* dev) } msi_free_vector(dev, vector, 0); if (warning) { - /* Force to release the MSI-X memory-mapped table */ -#if 0 - unsigned long phys_addr; - u32 table_offset; - u16 control; - u8 bir; - - pci_read_config_word(dev, msi_control_reg(pos), - &control); - pci_read_config_dword(dev, msix_table_offset_reg(pos), - &table_offset); - bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK); - table_offset &= ~PCI_MSIX_FLAGS_BIRMASK; - phys_addr = pci_resource_start(dev, bir) + table_offset; -/* - * FIXME! and what did you want to do with phys_addr? - */ -#endif iounmap(base); printk(KERN_WARNING "PCI: %s: msi_remove_pci_irq_vectors() " "called without free_irq() on all MSI-X vectors\n", diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h index 4ac52d441e4..56951c39d3a 100644 --- a/drivers/pci/msi.h +++ b/drivers/pci/msi.h @@ -6,6 +6,68 @@ #ifndef MSI_H #define MSI_H +/* + * MSI operation vector. Used by the msi core code (drivers/pci/msi.c) + * to abstract platform-specific tasks relating to MSI address generation + * and resource management. + */ +struct msi_ops { + /** + * setup - generate an MSI bus address and data for a given vector + * @pdev: PCI device context (in) + * @vector: vector allocated by the msi core (in) + * @addr_hi: upper 32 bits of PCI bus MSI address (out) + * @addr_lo: lower 32 bits of PCI bus MSI address (out) + * @data: MSI data payload (out) + * + * Description: The setup op is used to generate a PCI bus addres and + * data which the msi core will program into the card MSI capability + * registers. The setup routine is responsible for picking an initial + * cpu to target the MSI at. The setup routine is responsible for + * examining pdev to determine the MSI capabilities of the card and + * generating a suitable address/data. The setup routine is + * responsible for allocating and tracking any system resources it + * needs to route the MSI to the cpu it picks, and for associating + * those resources with the passed in vector. + * + * Returns 0 if the MSI address/data was successfully setup. + **/ + + int (*setup) (struct pci_dev *pdev, unsigned int vector, + u32 *addr_hi, u32 *addr_lo, u32 *data); + + /** + * teardown - release resources allocated by setup + * @vector: vector context for resources (in) + * + * Description: The teardown op is used to release any resources + * that were allocated in the setup routine associated with the passed + * in vector. + **/ + + void (*teardown) (unsigned int vector); + + /** + * target - retarget an MSI at a different cpu + * @vector: vector context for resources (in) + * @cpu: new cpu to direct vector at (in) + * @addr_hi: new value of PCI bus upper 32 bits (in/out) + * @addr_lo: new value of PCI bus lower 32 bits (in/out) + * + * Description: The target op is used to redirect an MSI vector + * at a different cpu. addr_hi/addr_lo coming in are the existing + * values that the MSI core has programmed into the card. The + * target code is responsible for freeing any resources (if any) + * associated with the old address, and generating a new PCI bus + * addr_hi/addr_lo that will redirect the vector at the indicated cpu. + **/ + + void (*target) (unsigned int vector, unsigned int cpu, + u32 *addr_hi, u32 *addr_lo); +}; + +extern int msi_register(struct msi_ops *ops); + #include <asm/msi.h> /* @@ -63,67 +125,6 @@ extern int pci_vector_resources(int last, int nr_released); #define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK) #define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK) -/* - * MSI Defined Data Structures - */ -#define MSI_ADDRESS_HEADER 0xfee -#define MSI_ADDRESS_HEADER_SHIFT 12 -#define MSI_ADDRESS_HEADER_MASK 0xfff000 -#define MSI_ADDRESS_DEST_ID_MASK 0xfff0000f -#define MSI_TARGET_CPU_MASK 0xff -#define MSI_DELIVERY_MODE 0 -#define MSI_LEVEL_MODE 1 /* Edge always assert */ -#define MSI_TRIGGER_MODE 0 /* MSI is edge sensitive */ -#define MSI_PHYSICAL_MODE 0 -#define MSI_LOGICAL_MODE 1 -#define MSI_REDIRECTION_HINT_MODE 0 - -struct msg_data { -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u32 vector : 8; - __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */ - __u32 reserved_1 : 3; - __u32 level : 1; /* 0: deassert | 1: assert */ - __u32 trigger : 1; /* 0: edge | 1: level */ - __u32 reserved_2 : 16; -#elif defined(__BIG_ENDIAN_BITFIELD) - __u32 reserved_2 : 16; - __u32 trigger : 1; /* 0: edge | 1: level */ - __u32 level : 1; /* 0: deassert | 1: assert */ - __u32 reserved_1 : 3; - __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */ - __u32 vector : 8; -#else -#error "Bitfield endianness not defined! Check your byteorder.h" -#endif -} __attribute__ ((packed)); - -struct msg_address { - union { - struct { -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u32 reserved_1 : 2; - __u32 dest_mode : 1; /*0:physic | 1:logic */ - __u32 redirection_hint: 1; /*0: dedicated CPU - 1: lowest priority */ - __u32 reserved_2 : 4; - __u32 dest_id : 24; /* Destination ID */ -#elif defined(__BIG_ENDIAN_BITFIELD) - __u32 dest_id : 24; /* Destination ID */ - __u32 reserved_2 : 4; - __u32 redirection_hint: 1; /*0: dedicated CPU - 1: lowest priority */ - __u32 dest_mode : 1; /*0:physic | 1:logic */ - __u32 reserved_1 : 2; -#else -#error "Bitfield endianness not defined! Check your byteorder.h" -#endif - }u; - __u32 value; - }lo_address; - __u32 hi_address; -} __attribute__ ((packed)); - struct msi_desc { struct { __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */ @@ -132,7 +133,7 @@ struct msi_desc { __u8 reserved: 1; /* reserved */ __u8 entry_nr; /* specific enabled entry */ __u8 default_vector; /* default pre-assigned vector */ - __u8 current_cpu; /* current destination cpu */ + __u8 unused; /* formerly unused destination cpu*/ }msi_attrib; struct { @@ -142,6 +143,14 @@ struct msi_desc { void __iomem *mask_base; struct pci_dev *dev; + +#ifdef CONFIG_PM + /* PM save area for MSIX address/data */ + + u32 address_hi_save; + u32 address_lo_save; + u32 data_save; +#endif }; #endif /* MSI_H */ diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index c2ecae5ff0c..bb7456c1dba 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -267,7 +267,7 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) /* ACPI bus type */ -static int pci_acpi_find_device(struct device *dev, acpi_handle *handle) +static int acpi_pci_find_device(struct device *dev, acpi_handle *handle) { struct pci_dev * pci_dev; acpi_integer addr; @@ -281,7 +281,7 @@ static int pci_acpi_find_device(struct device *dev, acpi_handle *handle) return 0; } -static int pci_acpi_find_root_bridge(struct device *dev, acpi_handle *handle) +static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle) { int num; unsigned int seg, bus; @@ -299,21 +299,21 @@ static int pci_acpi_find_root_bridge(struct device *dev, acpi_handle *handle) return 0; } -static struct acpi_bus_type pci_acpi_bus = { +static struct acpi_bus_type acpi_pci_bus = { .bus = &pci_bus_type, - .find_device = pci_acpi_find_device, - .find_bridge = pci_acpi_find_root_bridge, + .find_device = acpi_pci_find_device, + .find_bridge = acpi_pci_find_root_bridge, }; -static int __init pci_acpi_init(void) +static int __init acpi_pci_init(void) { int ret; - ret = register_acpi_bus_type(&pci_acpi_bus); + ret = register_acpi_bus_type(&acpi_pci_bus); if (ret) return 0; platform_pci_choose_state = acpi_pci_choose_state; platform_pci_set_power_state = acpi_pci_set_power_state; return 0; } -arch_initcall(pci_acpi_init); +arch_initcall(acpi_pci_init); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 56ac2bc003c..bc405c035ce 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -43,6 +43,29 @@ pci_config_attr(subsystem_vendor, "0x%04x\n"); pci_config_attr(subsystem_device, "0x%04x\n"); pci_config_attr(class, "0x%06x\n"); pci_config_attr(irq, "%u\n"); +pci_config_attr(is_enabled, "%u\n"); + +static ssize_t broken_parity_status_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + return sprintf (buf, "%u\n", pdev->broken_parity_status); +} + +static ssize_t broken_parity_status_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + ssize_t consumed = -EINVAL; + + if ((count > 0) && (*buf == '0' || *buf == '1')) { + pdev->broken_parity_status = *buf == '1' ? 1 : 0; + consumed = count; + } + return consumed; +} static ssize_t local_cpus_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -90,6 +113,25 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8), (u8)(pci_dev->class)); } +static ssize_t +is_enabled_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + /* this can crash the machine when done on the "wrong" device */ + if (!capable(CAP_SYS_ADMIN)) + return count; + + if (*buf == '0') + pci_disable_device(pdev); + + if (*buf == '1') + pci_enable_device(pdev); + + return count; +} + struct device_attribute pci_dev_attrs[] = { __ATTR_RO(resource), @@ -101,6 +143,9 @@ struct device_attribute pci_dev_attrs[] = { __ATTR_RO(irq), __ATTR_RO(local_cpus), __ATTR_RO(modalias), + __ATTR(enable, 0600, is_enabled_show, is_enabled_store), + __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), + broken_parity_status_show,broken_parity_status_store), __ATTR_NULL, }; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index fde41cc1473..d408a3c3042 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -517,7 +517,12 @@ pci_enable_device_bars(struct pci_dev *dev, int bars) int pci_enable_device(struct pci_dev *dev) { - int err = pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1); + int err; + + if (dev->is_enabled) + return 0; + + err = pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1); if (err) return err; pci_fixup_device(pci_fixup_enable, dev); @@ -546,7 +551,14 @@ void pci_disable_device(struct pci_dev *dev) { u16 pci_command; - + + if (dev->msi_enabled) + disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), + PCI_CAP_ID_MSI); + if (dev->msix_enabled) + disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), + PCI_CAP_ID_MSIX); + pci_read_config_word(dev, PCI_COMMAND, &pci_command); if (pci_command & PCI_COMMAND_MASTER) { pci_command &= ~PCI_COMMAND_MASTER; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 30630cbe2fe..29bdeca031a 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -40,7 +40,7 @@ extern int pci_bus_find_capability (struct pci_bus *bus, unsigned int devfn, int extern void pci_remove_legacy_files(struct pci_bus *bus); /* Lock for read/write access to pci device and bus lists */ -extern spinlock_t pci_bus_lock; +extern struct rw_semaphore pci_bus_sem; #ifdef CONFIG_X86_IO_APIC extern int pci_msi_quirk; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index a10ed9dab2c..f89dbc3738b 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -180,25 +180,31 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) res->flags |= pci_calc_resource_flags(l); if ((l & (PCI_BASE_ADDRESS_SPACE | PCI_BASE_ADDRESS_MEM_TYPE_MASK)) == (PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64)) { - pci_read_config_dword(dev, reg+4, &l); + u32 szhi, lhi; + pci_read_config_dword(dev, reg+4, &lhi); + pci_write_config_dword(dev, reg+4, ~0); + pci_read_config_dword(dev, reg+4, &szhi); + pci_write_config_dword(dev, reg+4, lhi); + szhi = pci_size(lhi, szhi, 0xffffffff); next++; #if BITS_PER_LONG == 64 - res->start |= ((unsigned long) l) << 32; + res->start |= ((unsigned long) lhi) << 32; res->end = res->start + sz; - pci_write_config_dword(dev, reg+4, ~0); - pci_read_config_dword(dev, reg+4, &sz); - pci_write_config_dword(dev, reg+4, l); - sz = pci_size(l, sz, 0xffffffff); - if (sz) { + if (szhi) { /* This BAR needs > 4GB? Wow. */ - res->end |= (unsigned long)sz<<32; + res->end |= (unsigned long)szhi<<32; } #else - if (l) { - printk(KERN_ERR "PCI: Unable to handle 64-bit address for device %s\n", pci_name(dev)); + if (szhi) { + printk(KERN_ERR "PCI: Unable to handle 64-bit BAR for device %s\n", pci_name(dev)); res->start = 0; res->flags = 0; - continue; + } else if (lhi) { + /* 64-bit wide address, treat as disabled */ + pci_write_config_dword(dev, reg, l & ~(u32)PCI_BASE_ADDRESS_MEM_MASK); + pci_write_config_dword(dev, reg+4, 0); + res->start = 0; + res->end = sz; } #endif } @@ -377,9 +383,9 @@ struct pci_bus * __devinit pci_add_new_bus(struct pci_bus *parent, struct pci_de child = pci_alloc_child_bus(parent, dev, busnr); if (child) { - spin_lock(&pci_bus_lock); + down_write(&pci_bus_sem); list_add_tail(&child->node, &parent->children); - spin_unlock(&pci_bus_lock); + up_write(&pci_bus_sem); } return child; } @@ -838,9 +844,9 @@ void __devinit pci_device_add(struct pci_dev *dev, struct pci_bus *bus) * and the bus list for fixup functions, etc. */ INIT_LIST_HEAD(&dev->global_list); - spin_lock(&pci_bus_lock); + down_write(&pci_bus_sem); list_add_tail(&dev->bus_list, &bus->devices); - spin_unlock(&pci_bus_lock); + up_write(&pci_bus_sem); } struct pci_dev * __devinit @@ -975,9 +981,10 @@ struct pci_bus * __devinit pci_create_bus(struct device *parent, pr_debug("PCI: Bus %04x:%02x already known\n", pci_domain_nr(b), bus); goto err_out; } - spin_lock(&pci_bus_lock); + + down_write(&pci_bus_sem); list_add_tail(&b->node, &pci_root_buses); - spin_unlock(&pci_bus_lock); + up_write(&pci_bus_sem); memset(dev, 0, sizeof(*dev)); dev->parent = parent; @@ -1017,9 +1024,9 @@ class_dev_create_file_err: class_dev_reg_err: device_unregister(dev); dev_reg_err: - spin_lock(&pci_bus_lock); + down_write(&pci_bus_sem); list_del(&b->node); - spin_unlock(&pci_bus_lock); + up_write(&pci_bus_sem); err_out: kfree(dev); kfree(b); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index d378478612f..4364d793f73 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -24,6 +24,17 @@ #include <linux/acpi.h> #include "pci.h" +/* The Mellanox Tavor device gives false positive parity errors + * Mark this device with a broken_parity_status, to allow + * PCI scanning code to "skip" this now blacklisted device. + */ +static void __devinit quirk_mellanox_tavor(struct pci_dev *dev) +{ + dev->broken_parity_status = 1; /* This device gives false positives */ +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX,PCI_DEVICE_ID_MELLANOX_TAVOR,quirk_mellanox_tavor); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_MELLANOX,PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE,quirk_mellanox_tavor); + /* Deal with broken BIOS'es that neglect to enable passive release, which can cause problems in combination with the 82441FX/PPro MTRRs */ static void __devinit quirk_passive_release(struct pci_dev *dev) @@ -878,27 +889,30 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82375, quirk_e * when a PCI-Soundcard is added. The BIOS only gives Options * "Disabled" and "AUTO". This Quirk Sets the corresponding * Register-Value to enable the Soundcard. + * + * FIXME: Presently this quirk will run on anything that has an 8237 + * which isn't correct, we need to check DMI tables or something in + * order to make sure it only runs on the MSI-K8T-Neo2Fir. Because it + * runs everywhere at present we suppress the printk output in most + * irrelevant cases. */ static void __init k8t_sound_hostbridge(struct pci_dev *dev) { unsigned char val; - printk(KERN_INFO "PCI: Quirk-MSI-K8T Soundcard On\n"); pci_read_config_byte(dev, 0x50, &val); if (val == 0x88 || val == 0xc8) { + /* Assume it's probably a MSI-K8T-Neo2Fir */ + printk(KERN_INFO "PCI: MSI-K8T-Neo2Fir, attempting to turn soundcard ON\n"); pci_write_config_byte(dev, 0x50, val & (~0x40)); /* Verify the Change for Status output */ pci_read_config_byte(dev, 0x50, &val); if (val & 0x40) - printk(KERN_INFO "PCI: MSI-K8T soundcard still off\n"); + printk(KERN_INFO "PCI: MSI-K8T-Neo2Fir, soundcard still off\n"); else - printk(KERN_INFO "PCI: MSI-K8T soundcard on\n"); - } else { - printk(KERN_INFO "PCI: Unexpected Value in PCI-Register: " - "no Change!\n"); + printk(KERN_INFO "PCI: MSI-K8T-Neo2Fir, soundcard on\n"); } - } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, k8t_sound_hostbridge); @@ -1485,6 +1499,25 @@ static void __devinit quirk_p64h2_1k_io(struct pci_dev *dev) } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1460, quirk_p64h2_1k_io); +/* Under some circumstances, AER is not linked with extended capabilities. + * Force it to be linked by setting the corresponding control bit in the + * config space. + */ +static void __devinit quirk_nvidia_ck804_pcie_aer_ext_cap(struct pci_dev *dev) +{ + uint8_t b; + if (pci_read_config_byte(dev, 0xf41, &b) == 0) { + if (!(b & 0x20)) { + pci_write_config_byte(dev, 0xf41, b | 0x20); + printk(KERN_INFO + "PCI: Linking AER extended capability on %s\n", + pci_name(dev)); + } + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, + quirk_nvidia_ck804_pcie_aer_ext_cap); + EXPORT_SYMBOL(pcie_mch_quirk); #ifdef CONFIG_HOTPLUG EXPORT_SYMBOL(pci_fixup_device); diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 1a6bf9de166..99ffbd478b2 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -22,18 +22,18 @@ static void pci_destroy_dev(struct pci_dev *dev) pci_proc_detach_device(dev); pci_remove_sysfs_dev_files(dev); device_unregister(&dev->dev); - spin_lock(&pci_bus_lock); + down_write(&pci_bus_sem); list_del(&dev->global_list); dev->global_list.next = dev->global_list.prev = NULL; - spin_unlock(&pci_bus_lock); + up_write(&pci_bus_sem); } /* Remove the device from the device lists, and prevent any further * list accesses from this device */ - spin_lock(&pci_bus_lock); + down_write(&pci_bus_sem); list_del(&dev->bus_list); dev->bus_list.next = dev->bus_list.prev = NULL; - spin_unlock(&pci_bus_lock); + up_write(&pci_bus_sem); pci_free_resources(dev); pci_dev_put(dev); @@ -62,9 +62,9 @@ void pci_remove_bus(struct pci_bus *pci_bus) { pci_proc_detach_bus(pci_bus); - spin_lock(&pci_bus_lock); + down_write(&pci_bus_sem); list_del(&pci_bus->node); - spin_unlock(&pci_bus_lock); + up_write(&pci_bus_sem); pci_remove_legacy_files(pci_bus); class_device_remove_file(&pci_bus->class_dev, &class_device_attr_cpuaffinity); diff --git a/drivers/pci/search.c b/drivers/pci/search.c index ce7dd6e7be6..622b3f8ba82 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -13,7 +13,7 @@ #include <linux/interrupt.h> #include "pci.h" -DEFINE_SPINLOCK(pci_bus_lock); +DECLARE_RWSEM(pci_bus_sem); static struct pci_bus * __devinit pci_do_find_bus(struct pci_bus* bus, unsigned char busnr) @@ -72,11 +72,11 @@ pci_find_next_bus(const struct pci_bus *from) struct pci_bus *b = NULL; WARN_ON(in_interrupt()); - spin_lock(&pci_bus_lock); + down_read(&pci_bus_sem); n = from ? from->node.next : pci_root_buses.next; if (n != &pci_root_buses) b = pci_bus_b(n); - spin_unlock(&pci_bus_lock); + up_read(&pci_bus_sem); return b; } @@ -124,7 +124,7 @@ struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn) struct pci_dev *dev; WARN_ON(in_interrupt()); - spin_lock(&pci_bus_lock); + down_read(&pci_bus_sem); list_for_each(tmp, &bus->devices) { dev = pci_dev_b(tmp); @@ -135,7 +135,7 @@ struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn) dev = NULL; out: pci_dev_get(dev); - spin_unlock(&pci_bus_lock); + up_read(&pci_bus_sem); return dev; } @@ -167,7 +167,7 @@ static struct pci_dev * pci_find_subsys(unsigned int vendor, struct pci_dev *dev; WARN_ON(in_interrupt()); - spin_lock(&pci_bus_lock); + down_read(&pci_bus_sem); n = from ? from->global_list.next : pci_devices.next; while (n && (n != &pci_devices)) { @@ -181,7 +181,7 @@ static struct pci_dev * pci_find_subsys(unsigned int vendor, } dev = NULL; exit: - spin_unlock(&pci_bus_lock); + up_read(&pci_bus_sem); return dev; } @@ -232,7 +232,7 @@ pci_get_subsys(unsigned int vendor, unsigned int device, struct pci_dev *dev; WARN_ON(in_interrupt()); - spin_lock(&pci_bus_lock); + down_read(&pci_bus_sem); n = from ? from->global_list.next : pci_devices.next; while (n && (n != &pci_devices)) { @@ -247,7 +247,7 @@ pci_get_subsys(unsigned int vendor, unsigned int device, dev = NULL; exit: dev = pci_dev_get(dev); - spin_unlock(&pci_bus_lock); + up_read(&pci_bus_sem); pci_dev_put(from); return dev; } @@ -292,7 +292,7 @@ pci_find_device_reverse(unsigned int vendor, unsigned int device, const struct p struct pci_dev *dev; WARN_ON(in_interrupt()); - spin_lock(&pci_bus_lock); + down_read(&pci_bus_sem); n = from ? from->global_list.prev : pci_devices.prev; while (n && (n != &pci_devices)) { @@ -304,7 +304,7 @@ pci_find_device_reverse(unsigned int vendor, unsigned int device, const struct p } dev = NULL; exit: - spin_unlock(&pci_bus_lock); + up_read(&pci_bus_sem); return dev; } @@ -328,7 +328,7 @@ struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from) struct pci_dev *dev; WARN_ON(in_interrupt()); - spin_lock(&pci_bus_lock); + down_read(&pci_bus_sem); n = from ? from->global_list.next : pci_devices.next; while (n && (n != &pci_devices)) { @@ -340,7 +340,7 @@ struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from) dev = NULL; exit: dev = pci_dev_get(dev); - spin_unlock(&pci_bus_lock); + up_read(&pci_bus_sem); pci_dev_put(from); return dev; } @@ -362,7 +362,7 @@ int pci_dev_present(const struct pci_device_id *ids) int found = 0; WARN_ON(in_interrupt()); - spin_lock(&pci_bus_lock); + down_read(&pci_bus_sem); while (ids->vendor || ids->subvendor || ids->class_mask) { list_for_each_entry(dev, &pci_devices, global_list) { if (pci_match_one_device(ids, dev)) { @@ -372,8 +372,8 @@ int pci_dev_present(const struct pci_device_id *ids) } ids++; } -exit: - spin_unlock(&pci_bus_lock); +exit: + up_read(&pci_bus_sem); return found; } EXPORT_SYMBOL(pci_dev_present); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 28ce3a7ee43..35086e80faa 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -55,9 +55,10 @@ pbus_assign_resources_sorted(struct pci_bus *bus) list_for_each_entry(dev, &bus->devices, bus_list) { u16 class = dev->class >> 8; - /* Don't touch classless devices and host bridges. */ + /* Don't touch classless devices or host bridges or ioapics. */ if (class == PCI_CLASS_NOT_DEFINED || - class == PCI_CLASS_BRIDGE_HOST) + class == PCI_CLASS_BRIDGE_HOST || + class == PCI_CLASS_SYSTEM_PIC) continue; pdev_sort_resources(dev, &head); diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index ea9277b7f89..577f4b55c46 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -155,6 +155,46 @@ int pci_assign_resource(struct pci_dev *dev, int resno) return ret; } +#ifdef CONFIG_EMBEDDED +int pci_assign_resource_fixed(struct pci_dev *dev, int resno) +{ + struct pci_bus *bus = dev->bus; + struct resource *res = dev->resource + resno; + unsigned int type_mask; + int i, ret = -EBUSY; + + type_mask = IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; + + for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { + struct resource *r = bus->resource[i]; + if (!r) + continue; + + /* type_mask must match */ + if ((res->flags ^ r->flags) & type_mask) + continue; + + ret = request_resource(r, res); + + if (ret == 0) + break; + } + + if (ret) { + printk(KERN_ERR "PCI: Failed to allocate %s resource " + "#%d:%llx@%llx for %s\n", + res->flags & IORESOURCE_IO ? "I/O" : "mem", + resno, (unsigned long long)(res->end - res->start + 1), + (unsigned long long)res->start, pci_name(dev)); + } else if (resno < PCI_BRIDGE_RESOURCES) { + pci_update_resource(dev, res, resno); + } + + return ret; +} +EXPORT_SYMBOL_GPL(pci_assign_resource_fixed); +#endif + /* Sort resources by alignment */ void __devinit pdev_sort_resources(struct pci_dev *dev, struct resource_list *head) diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c index 77bb2351500..680f6063954 100644 --- a/drivers/scsi/qla1280.c +++ b/drivers/scsi/qla1280.c @@ -397,30 +397,6 @@ #include "ql1280_fw.h" #include "ql1040_fw.h" - -/* - * Missing PCI ID's - */ -#ifndef PCI_DEVICE_ID_QLOGIC_ISP1080 -#define PCI_DEVICE_ID_QLOGIC_ISP1080 0x1080 -#endif -#ifndef PCI_DEVICE_ID_QLOGIC_ISP1240 -#define PCI_DEVICE_ID_QLOGIC_ISP1240 0x1240 -#endif -#ifndef PCI_DEVICE_ID_QLOGIC_ISP1280 -#define PCI_DEVICE_ID_QLOGIC_ISP1280 0x1280 -#endif -#ifndef PCI_DEVICE_ID_QLOGIC_ISP10160 -#define PCI_DEVICE_ID_QLOGIC_ISP10160 0x1016 -#endif -#ifndef PCI_DEVICE_ID_QLOGIC_ISP12160 -#define PCI_DEVICE_ID_QLOGIC_ISP12160 0x1216 -#endif - -#ifndef PCI_VENDOR_ID_AMI -#define PCI_VENDOR_ID_AMI 0x101e -#endif - #ifndef BITS_PER_LONG #error "BITS_PER_LONG not defined!" #endif diff --git a/drivers/scsi/sata_vsc.c b/drivers/scsi/sata_vsc.c index 8a29ce340b4..27d658704cf 100644 --- a/drivers/scsi/sata_vsc.c +++ b/drivers/scsi/sata_vsc.c @@ -433,13 +433,14 @@ err_out: /* - * 0x1725/0x7174 is the Vitesse VSC-7174 - * 0x8086/0x3200 is the Intel 31244, which is supposed to be identical - * compatibility is untested as of yet + * Intel 31244 is supposed to be identical. + * Compatibility is untested as of yet. */ static const struct pci_device_id vsc_sata_pci_tbl[] = { - { 0x1725, 0x7174, PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 }, - { 0x8086, 0x3200, PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 }, + { PCI_VENDOR_ID_VITESSE, PCI_DEVICE_ID_VITESSE_VSC7174, + PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 }, + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GD31244, + PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 }, { } }; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 5ea778fc1ca..bef4a9622ed 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -937,4 +937,23 @@ config SERIAL_SGI_IOC3 If you have an SGI Altix with an IOC3 serial card, say Y or M. Otherwise, say N. +config SERIAL_NETX + bool "NetX serial port support" + depends on ARM && ARCH_NETX + select SERIAL_CORE + help + If you have a machine based on a Hilscher NetX SoC you + can enable its onboard serial port by enabling this option. + + To compile this driver as a module, choose M here: the + module will be called netx-serial. + +config SERIAL_NETX_CONSOLE + bool "Console on NetX serial port" + depends on SERIAL_NETX + select SERIAL_CORE_CONSOLE + help + If you have enabled the serial port on the Motorola IMX + CPU you can make it the console by answering Y to this option. + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 0a71bf68a03..927faee0362 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -55,3 +55,4 @@ obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o obj-$(CONFIG_SERIAL_AT91) += at91_serial.o +obj-$(CONFIG_SERIAL_NETX) += netx-serial.o diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c index 1631414000a..e920d196d0b 100644 --- a/drivers/serial/amba-pl010.c +++ b/drivers/serial/amba-pl010.c @@ -52,7 +52,7 @@ #include <asm/io.h> -#define UART_NR 2 +#define UART_NR 8 #define SERIAL_AMBA_MAJOR 204 #define SERIAL_AMBA_MINOR 16 diff --git a/drivers/serial/netx-serial.c b/drivers/serial/netx-serial.c new file mode 100644 index 00000000000..c1adc9e4b23 --- /dev/null +++ b/drivers/serial/netx-serial.c @@ -0,0 +1,749 @@ +/* + * drivers/serial/netx-serial.c + * + * Copyright (c) 2005 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/config.h> + +#if defined(CONFIG_SERIAL_NETX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/platform_device.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_core.h> +#include <linux/serial.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/hardware.h> +#include <asm/arch/netx-regs.h> + +/* We've been assigned a range on the "Low-density serial ports" major */ +#define SERIAL_NX_MAJOR 204 +#define MINOR_START 170 + +#ifdef CONFIG_SERIAL_NETX_CONSOLE + +enum uart_regs { + UART_DR = 0x00, + UART_SR = 0x04, + UART_LINE_CR = 0x08, + UART_BAUDDIV_MSB = 0x0c, + UART_BAUDDIV_LSB = 0x10, + UART_CR = 0x14, + UART_FR = 0x18, + UART_IIR = 0x1c, + UART_ILPR = 0x20, + UART_RTS_CR = 0x24, + UART_RTS_LEAD = 0x28, + UART_RTS_TRAIL = 0x2c, + UART_DRV_ENABLE = 0x30, + UART_BRM_CR = 0x34, + UART_RXFIFO_IRQLEVEL = 0x38, + UART_TXFIFO_IRQLEVEL = 0x3c, +}; + +#define SR_FE (1<<0) +#define SR_PE (1<<1) +#define SR_BE (1<<2) +#define SR_OE (1<<3) + +#define LINE_CR_BRK (1<<0) +#define LINE_CR_PEN (1<<1) +#define LINE_CR_EPS (1<<2) +#define LINE_CR_STP2 (1<<3) +#define LINE_CR_FEN (1<<4) +#define LINE_CR_5BIT (0<<5) +#define LINE_CR_6BIT (1<<5) +#define LINE_CR_7BIT (2<<5) +#define LINE_CR_8BIT (3<<5) +#define LINE_CR_BITS_MASK (3<<5) + +#define CR_UART_EN (1<<0) +#define CR_SIREN (1<<1) +#define CR_SIRLP (1<<2) +#define CR_MSIE (1<<3) +#define CR_RIE (1<<4) +#define CR_TIE (1<<5) +#define CR_RTIE (1<<6) +#define CR_LBE (1<<7) + +#define FR_CTS (1<<0) +#define FR_DSR (1<<1) +#define FR_DCD (1<<2) +#define FR_BUSY (1<<3) +#define FR_RXFE (1<<4) +#define FR_TXFF (1<<5) +#define FR_RXFF (1<<6) +#define FR_TXFE (1<<7) + +#define IIR_MIS (1<<0) +#define IIR_RIS (1<<1) +#define IIR_TIS (1<<2) +#define IIR_RTIS (1<<3) +#define IIR_MASK 0xf + +#define RTS_CR_AUTO (1<<0) +#define RTS_CR_RTS (1<<1) +#define RTS_CR_COUNT (1<<2) +#define RTS_CR_MOD2 (1<<3) +#define RTS_CR_RTS_POL (1<<4) +#define RTS_CR_CTS_CTR (1<<5) +#define RTS_CR_CTS_POL (1<<6) +#define RTS_CR_STICK (1<<7) + +#define UART_PORT_SIZE 0x40 +#define DRIVER_NAME "netx-uart" + +struct netx_port { + struct uart_port port; +}; + +static void netx_stop_tx(struct uart_port *port) +{ + unsigned int val; + val = readl(port->membase + UART_CR); + writel(val & ~CR_TIE, port->membase + UART_CR); +} + +static void netx_stop_rx(struct uart_port *port) +{ + unsigned int val; + val = readl(port->membase + UART_CR); + writel(val & ~CR_RIE, port->membase + UART_CR); +} + +static void netx_enable_ms(struct uart_port *port) +{ + unsigned int val; + val = readl(port->membase + UART_CR); + writel(val | CR_MSIE, port->membase + UART_CR); +} + +static inline void netx_transmit_buffer(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + + if (port->x_char) { + writel(port->x_char, port->membase + UART_DR); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_tx_stopped(port) || uart_circ_empty(xmit)) { + netx_stop_tx(port); + return; + } + + do { + /* send xmit->buf[xmit->tail] + * out the port here */ + writel(xmit->buf[xmit->tail], port->membase + UART_DR); + xmit->tail = (xmit->tail + 1) & + (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (!(readl(port->membase + UART_FR) & FR_TXFF)); + + if (uart_circ_empty(xmit)) + netx_stop_tx(port); +} + +static void netx_start_tx(struct uart_port *port) +{ + writel( + readl(port->membase + UART_CR) | CR_TIE, port->membase + UART_CR); + + if (!(readl(port->membase + UART_FR) & FR_TXFF)) + netx_transmit_buffer(port); +} + +static unsigned int netx_tx_empty(struct uart_port *port) +{ + return readl(port->membase + UART_FR) & FR_BUSY ? 0 : TIOCSER_TEMT; +} + +static void netx_txint(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + netx_stop_tx(port); + return; + } + + netx_transmit_buffer(port); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static void netx_rxint(struct uart_port *port, struct pt_regs *regs) +{ + unsigned char rx, flg, status; + struct tty_struct *tty = port->info->tty; + + while (!(readl(port->membase + UART_FR) & FR_RXFE)) { + rx = readl(port->membase + UART_DR); + flg = TTY_NORMAL; + port->icount.rx++; + status = readl(port->membase + UART_SR); + if (status & SR_BE) { + writel(0, port->membase + UART_SR); + if (uart_handle_break(port)) + continue; + } + + if (unlikely(status & (SR_FE | SR_PE | SR_OE))) { + + if (status & SR_PE) + port->icount.parity++; + else if (status & SR_FE) + port->icount.frame++; + if (status & SR_OE) + port->icount.overrun++; + + status &= port->read_status_mask; + + if (status & SR_BE) + flg = TTY_BREAK; + else if (status & SR_PE) + flg = TTY_PARITY; + else if (status & SR_FE) + flg = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, rx, regs)) + continue; + + uart_insert_char(port, status, SR_OE, rx, flg); + } + + tty_flip_buffer_push(tty); + return; +} + +static irqreturn_t netx_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct uart_port *port = (struct uart_port *)dev_id; + unsigned long flags; + unsigned char status; + + spin_lock_irqsave(&port->lock,flags); + + status = readl(port->membase + UART_IIR) & IIR_MASK; + while (status) { + if (status & IIR_RIS) + netx_rxint(port, regs); + if (status & IIR_TIS) + netx_txint(port); + if (status & IIR_MIS) { + if (readl(port->membase + UART_FR) & FR_CTS) + uart_handle_cts_change(port, 1); + else + uart_handle_cts_change(port, 0); + } + writel(0, port->membase + UART_IIR); + status = readl(port->membase + UART_IIR) & IIR_MASK; + } + + spin_unlock_irqrestore(&port->lock,flags); + return IRQ_HANDLED; +} + +static unsigned int netx_get_mctrl(struct uart_port *port) +{ + unsigned int ret = TIOCM_DSR | TIOCM_CAR; + + if (readl(port->membase + UART_FR) & FR_CTS) + ret |= TIOCM_CTS; + + return ret; +} + +static void netx_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned int val; + + if (mctrl & TIOCM_RTS) { + val = readl(port->membase + UART_RTS_CR); + writel(val | RTS_CR_RTS, port->membase + UART_RTS_CR); + } +} + +static void netx_break_ctl(struct uart_port *port, int break_state) +{ + unsigned int line_cr; + spin_lock_irq(&port->lock); + + line_cr = readl(port->membase + UART_LINE_CR); + if (break_state != 0) + line_cr |= LINE_CR_BRK; + else + line_cr &= ~LINE_CR_BRK; + writel(line_cr, port->membase + UART_LINE_CR); + + spin_unlock_irq(&port->lock); +} + +static int netx_startup(struct uart_port *port) +{ + int ret; + + ret = request_irq(port->irq, netx_int, 0, + DRIVER_NAME, port); + if (ret) { + dev_err(port->dev, "unable to grab irq%d\n",port->irq); + goto exit; + } + + writel(readl(port->membase + UART_LINE_CR) | LINE_CR_FEN, + port->membase + UART_LINE_CR); + + writel(CR_MSIE | CR_RIE | CR_TIE | CR_RTIE | CR_UART_EN, + port->membase + UART_CR); + +exit: + return ret; +} + +static void netx_shutdown(struct uart_port *port) +{ + writel(0, port->membase + UART_CR) ; + + free_irq(port->irq, port); +} + +static void +netx_set_termios(struct uart_port *port, struct termios *termios, + struct termios *old) +{ + unsigned int baud, quot; + unsigned char old_cr; + unsigned char line_cr = LINE_CR_FEN; + unsigned char rts_cr = 0; + + switch (termios->c_cflag & CSIZE) { + case CS5: + line_cr |= LINE_CR_5BIT; + break; + case CS6: + line_cr |= LINE_CR_6BIT; + break; + case CS7: + line_cr |= LINE_CR_7BIT; + break; + case CS8: + line_cr |= LINE_CR_8BIT; + break; + } + + if (termios->c_cflag & CSTOPB) + line_cr |= LINE_CR_STP2; + + if (termios->c_cflag & PARENB) { + line_cr |= LINE_CR_PEN; + if (!(termios->c_cflag & PARODD)) + line_cr |= LINE_CR_EPS; + } + + if (termios->c_cflag & CRTSCTS) + rts_cr = RTS_CR_AUTO | RTS_CR_CTS_CTR | RTS_CR_RTS_POL; + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = baud * 4096; + quot /= 1000; + quot *= 256; + quot /= 100000; + + spin_lock_irq(&port->lock); + + uart_update_timeout(port, termios->c_cflag, baud); + + old_cr = readl(port->membase + UART_CR); + + /* disable interrupts */ + writel(old_cr & ~(CR_MSIE | CR_RIE | CR_TIE | CR_RTIE), + port->membase + UART_CR); + + /* drain transmitter */ + while (readl(port->membase + UART_FR) & FR_BUSY); + + /* disable UART */ + writel(old_cr & ~CR_UART_EN, port->membase + UART_CR); + + /* modem status interrupts */ + old_cr &= ~CR_MSIE; + if (UART_ENABLE_MS(port, termios->c_cflag)) + old_cr |= CR_MSIE; + + writel((quot>>8) & 0xff, port->membase + UART_BAUDDIV_MSB); + writel(quot & 0xff, port->membase + UART_BAUDDIV_LSB); + writel(line_cr, port->membase + UART_LINE_CR); + + writel(rts_cr, port->membase + UART_RTS_CR); + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SR_PE; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= SR_BE; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= SR_PE; + } + + port->read_status_mask = 0; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= SR_BE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= SR_PE | SR_FE; + + writel(old_cr, port->membase + UART_CR); + + spin_unlock_irq(&port->lock); +} + +static const char *netx_type(struct uart_port *port) +{ + return port->type == PORT_NETX ? "NETX" : NULL; +} + +static void netx_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, UART_PORT_SIZE); +} + +static int netx_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, UART_PORT_SIZE, + DRIVER_NAME) != NULL ? 0 : -EBUSY; +} + +static void netx_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE && netx_request_port(port) == 0) + port->type = PORT_NETX; +} + +static int +netx_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_NETX) + ret = -EINVAL; + + return ret; +} + +static struct uart_ops netx_pops = { + .tx_empty = netx_tx_empty, + .set_mctrl = netx_set_mctrl, + .get_mctrl = netx_get_mctrl, + .stop_tx = netx_stop_tx, + .start_tx = netx_start_tx, + .stop_rx = netx_stop_rx, + .enable_ms = netx_enable_ms, + .break_ctl = netx_break_ctl, + .startup = netx_startup, + .shutdown = netx_shutdown, + .set_termios = netx_set_termios, + .type = netx_type, + .release_port = netx_release_port, + .request_port = netx_request_port, + .config_port = netx_config_port, + .verify_port = netx_verify_port, +}; + +static struct netx_port netx_ports[] = { + { + .port = { + .type = PORT_NETX, + .iotype = UPIO_MEM, + .membase = (char __iomem *)io_p2v(NETX_PA_UART0), + .mapbase = NETX_PA_UART0, + .irq = NETX_IRQ_UART0, + .uartclk = 100000000, + .fifosize = 16, + .flags = UPF_BOOT_AUTOCONF, + .ops = &netx_pops, + .line = 0, + }, + }, { + .port = { + .type = PORT_NETX, + .iotype = UPIO_MEM, + .membase = (char __iomem *)io_p2v(NETX_PA_UART1), + .mapbase = NETX_PA_UART1, + .irq = NETX_IRQ_UART1, + .uartclk = 100000000, + .fifosize = 16, + .flags = UPF_BOOT_AUTOCONF, + .ops = &netx_pops, + .line = 1, + }, + }, { + .port = { + .type = PORT_NETX, + .iotype = UPIO_MEM, + .membase = (char __iomem *)io_p2v(NETX_PA_UART2), + .mapbase = NETX_PA_UART2, + .irq = NETX_IRQ_UART2, + .uartclk = 100000000, + .fifosize = 16, + .flags = UPF_BOOT_AUTOCONF, + .ops = &netx_pops, + .line = 2, + }, + } +}; + +static void netx_console_putchar(struct uart_port *port, int ch) +{ + while (readl(port->membase + UART_FR) & FR_BUSY); + writel(ch, port->membase + UART_DR); +} + +static void +netx_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_port *port = &netx_ports[co->index].port; + unsigned char cr_save; + + cr_save = readl(port->membase + UART_CR); + writel(cr_save | CR_UART_EN, port->membase + UART_CR); + + uart_console_write(port, s, count, netx_console_putchar); + + while (readl(port->membase + UART_FR) & FR_BUSY); + writel(cr_save, port->membase + UART_CR); +} + +static void __init +netx_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits, int *flow) +{ + unsigned char line_cr; + + *baud = (readl(port->membase + UART_BAUDDIV_MSB) << 8) | + readl(port->membase + UART_BAUDDIV_LSB); + *baud *= 1000; + *baud /= 4096; + *baud *= 1000; + *baud /= 256; + *baud *= 100; + + line_cr = readl(port->membase + UART_LINE_CR); + *parity = 'n'; + if (line_cr & LINE_CR_PEN) { + if (line_cr & LINE_CR_EPS) + *parity = 'e'; + else + *parity = 'o'; + } + + switch (line_cr & LINE_CR_BITS_MASK) { + case LINE_CR_8BIT: + *bits = 8; + break; + case LINE_CR_7BIT: + *bits = 7; + break; + case LINE_CR_6BIT: + *bits = 6; + break; + case LINE_CR_5BIT: + *bits = 5; + break; + } + + if (readl(port->membase + UART_RTS_CR) & RTS_CR_AUTO) + *flow = 'r'; +} + +static int __init +netx_console_setup(struct console *co, char *options) +{ + struct netx_port *sport; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index == -1 || co->index >= ARRAY_SIZE(netx_ports)) + co->index = 0; + sport = &netx_ports[co->index]; + + if (options) { + uart_parse_options(options, &baud, &parity, &bits, &flow); + } else { + /* if the UART is enabled, assume it has been correctly setup + * by the bootloader and get the options + */ + if (readl(sport->port.membase + UART_CR) & CR_UART_EN) { + netx_console_get_options(&sport->port, &baud, + &parity, &bits, &flow); + } + + } + + return uart_set_options(&sport->port, co, baud, parity, bits, flow); +} + +static struct uart_driver netx_reg; +static struct console netx_console = { + .name = "ttyNX", + .write = netx_console_write, + .device = uart_console_device, + .setup = netx_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &netx_reg, +}; + +static int __init netx_console_init(void) +{ + register_console(&netx_console); + return 0; +} +console_initcall(netx_console_init); + +#define NETX_CONSOLE &netx_console +#else +#define NETX_CONSOLE NULL +#endif + +static struct uart_driver netx_reg = { + .owner = THIS_MODULE, + .driver_name = DRIVER_NAME, + .dev_name = "ttyNX", + .major = SERIAL_NX_MAJOR, + .minor = MINOR_START, + .nr = ARRAY_SIZE(netx_ports), + .cons = NETX_CONSOLE, +}; + +static int serial_netx_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct netx_port *sport = platform_get_drvdata(pdev); + + if (sport) + uart_suspend_port(&netx_reg, &sport->port); + + return 0; +} + +static int serial_netx_resume(struct platform_device *pdev) +{ + struct netx_port *sport = platform_get_drvdata(pdev); + + if (sport) + uart_resume_port(&netx_reg, &sport->port); + + return 0; +} + +static int serial_netx_probe(struct platform_device *pdev) +{ + struct uart_port *port = &netx_ports[pdev->id].port; + + dev_info(&pdev->dev, "initialising\n"); + + port->dev = &pdev->dev; + + writel(1, port->membase + UART_RXFIFO_IRQLEVEL); + uart_add_one_port(&netx_reg, &netx_ports[pdev->id].port); + platform_set_drvdata(pdev, &netx_ports[pdev->id]); + + return 0; +} + +static int serial_netx_remove(struct platform_device *pdev) +{ + struct netx_port *sport = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + if (sport) + uart_remove_one_port(&netx_reg, &sport->port); + + return 0; +} + +static struct platform_driver serial_netx_driver = { + .probe = serial_netx_probe, + .remove = serial_netx_remove, + + .suspend = serial_netx_suspend, + .resume = serial_netx_resume, + + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init netx_serial_init(void) +{ + int ret; + + printk(KERN_INFO "Serial: NetX driver\n"); + + ret = uart_register_driver(&netx_reg); + if (ret) + return ret; + + ret = platform_driver_register(&serial_netx_driver); + if (ret != 0) + uart_unregister_driver(&netx_reg); + + return 0; +} + +static void __exit netx_serial_exit(void) +{ + platform_driver_unregister(&serial_netx_driver); + uart_unregister_driver(&netx_reg); +} + +module_init(netx_serial_init); +module_exit(netx_serial_exit); + +MODULE_AUTHOR("Sascha Hauer"); +MODULE_DESCRIPTION("NetX serial port driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c index 989e4d49e5b..7f939d066a5 100644 --- a/drivers/video/console/mdacon.c +++ b/drivers/video/console/mdacon.c @@ -313,8 +313,8 @@ static const char __init *mdacon_startup(void) mda_num_columns = 80; mda_num_lines = 25; - mda_vram_base = VGA_MAP_MEM(0xb0000); mda_vram_len = 0x01000; + mda_vram_base = VGA_MAP_MEM(0xb0000, mda_vram_len); mda_index_port = 0x3b4; mda_value_port = 0x3b5; diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index d5a04b68c4d..e64d42e2449 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -391,7 +391,7 @@ static const char __init *vgacon_startup(void) static struct resource ega_console_resource = { "ega", 0x3B0, 0x3BF }; vga_video_type = VIDEO_TYPE_EGAM; - vga_vram_end = 0xb8000; + vga_vram_size = 0x8000; display_desc = "EGA+"; request_resource(&ioport_resource, &ega_console_resource); @@ -401,7 +401,7 @@ static const char __init *vgacon_startup(void) static struct resource mda2_console_resource = { "mda", 0x3BF, 0x3BF }; vga_video_type = VIDEO_TYPE_MDA; - vga_vram_end = 0xb2000; + vga_vram_size = 0x2000; display_desc = "*MDA"; request_resource(&ioport_resource, &mda1_console_resource); @@ -418,7 +418,7 @@ static const char __init *vgacon_startup(void) if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) { int i; - vga_vram_end = 0xc0000; + vga_vram_size = 0x8000; if (!ORIG_VIDEO_ISVGA) { static struct resource ega_console_resource @@ -443,7 +443,7 @@ static const char __init *vgacon_startup(void) * and COE=1 isn't necessarily a good idea) */ vga_vram_base = 0xa0000; - vga_vram_end = 0xb0000; + vga_vram_size = 0x10000; outb_p(6, VGA_GFX_I); outb_p(6, VGA_GFX_D); #endif @@ -475,7 +475,7 @@ static const char __init *vgacon_startup(void) static struct resource cga_console_resource = { "cga", 0x3D4, 0x3D5 }; vga_video_type = VIDEO_TYPE_CGA; - vga_vram_end = 0xba000; + vga_vram_size = 0x2000; display_desc = "*CGA"; request_resource(&ioport_resource, &cga_console_resource); @@ -483,9 +483,8 @@ static const char __init *vgacon_startup(void) } } - vga_vram_base = VGA_MAP_MEM(vga_vram_base); - vga_vram_end = VGA_MAP_MEM(vga_vram_end); - vga_vram_size = vga_vram_end - vga_vram_base; + vga_vram_base = VGA_MAP_MEM(vga_vram_base, vga_vram_size); + vga_vram_end = vga_vram_base + vga_vram_size; /* * Find out if there is a graphics card present. @@ -1020,14 +1019,14 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) char *charmap; if (vga_video_type != VIDEO_TYPE_EGAM) { - charmap = (char *) VGA_MAP_MEM(colourmap); + charmap = (char *) VGA_MAP_MEM(colourmap, 0); beg = 0x0e; #ifdef VGA_CAN_DO_64KB if (vga_video_type == VIDEO_TYPE_VGAC) beg = 0x06; #endif } else { - charmap = (char *) VGA_MAP_MEM(blackwmap); + charmap = (char *) VGA_MAP_MEM(blackwmap, 0); beg = 0x0a; } diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c index f3f16fd9f23..4fd2a272e03 100644 --- a/drivers/video/vga16fb.c +++ b/drivers/video/vga16fb.c @@ -1351,7 +1351,7 @@ static int __init vga16fb_probe(struct device *device) } /* XXX share VGA_FB_PHYS and I/O region with vgacon and others */ - info->screen_base = (void __iomem *)VGA_MAP_MEM(VGA_FB_PHYS); + info->screen_base = (void __iomem *)VGA_MAP_MEM(VGA_FB_PHYS, 0); if (!info->screen_base) { printk(KERN_ERR "vga16fb: unable to map device\n"); diff --git a/drivers/w1/Kconfig b/drivers/w1/Kconfig index 5e61ed59a41..f2d9a08e89a 100644 --- a/drivers/w1/Kconfig +++ b/drivers/w1/Kconfig @@ -3,7 +3,7 @@ menu "Dallas's 1-wire bus" config W1 tristate "Dallas's 1-wire support" ---help--- - Dallas's 1-wire bus is useful to connect slow 1-pin devices + Dallas' 1-wire bus is useful to connect slow 1-pin devices such as iButtons and thermal sensors. If you want W1 support, you should say Y here. @@ -11,6 +11,18 @@ config W1 This W1 support can also be built as a module. If so, the module will be called wire.ko. +config W1_CON + depends on CONNECTOR && W1 + bool "Userspace communication over connector" + default y + --- help --- + This allows to communicate with userspace using connector [Documentation/connector]. + There are three types of messages between w1 core and userspace: + 1. Events. They are generated each time new master or slave device found + either due to automatic or requested search. + 2. Userspace commands. Includes read/write and search/alarm search comamnds. + 3. Replies to userspace commands. + source drivers/w1/masters/Kconfig source drivers/w1/slaves/Kconfig diff --git a/drivers/w1/Makefile b/drivers/w1/Makefile index 0c2aa22d8c0..93845a2c7c2 100644 --- a/drivers/w1/Makefile +++ b/drivers/w1/Makefile @@ -2,10 +2,6 @@ # Makefile for the Dallas's 1-wire bus. # -ifneq ($(CONFIG_NET), y) -EXTRA_CFLAGS += -DNETLINK_DISABLED -endif - ifeq ($(CONFIG_W1_DS2433_CRC), y) EXTRA_CFLAGS += -DCONFIG_W1_F23_CRC endif diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index c6bad4dbdc6..2fb425536ea 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -15,24 +15,15 @@ config W1_MASTER_MATROX This support is also available as a module. If so, the module will be called matrox_w1.ko. -config W1_MASTER_DS9490 - tristate "DS9490R transport layer driver" - depends on W1 && USB - help - Say Y here if you want to have a driver for DS9490R UWB <-> W1 bridge. - - This support is also available as a module. If so, the module - will be called ds9490r.ko. - -config W1_MASTER_DS9490_BRIDGE - tristate "DS9490R USB <-> W1 transport layer for 1-wire" - depends on W1_MASTER_DS9490 - help - Say Y here if you want to communicate with your 1-wire devices - using DS9490R USB bridge. - - This support is also available as a module. If so, the module - will be called ds_w1_bridge.ko. +config W1_MASTER_DS2490 + tristate "DS2490 USB <-> W1 transport layer for 1-wire" + depends on W1 && USB + help + Say Y here if you want to have a driver for DS2490 based USB <-> W1 bridges, + for example DS9490*. + + This support is also available as a module. If so, the module + will be called ds2490.ko. config W1_MASTER_DS2482 tristate "Maxim DS2482 I2C to 1-Wire bridge" diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile index 1f3c8b983dc..4cee256a813 100644 --- a/drivers/w1/masters/Makefile +++ b/drivers/w1/masters/Makefile @@ -3,11 +3,6 @@ # obj-$(CONFIG_W1_MASTER_MATROX) += matrox_w1.o - -obj-$(CONFIG_W1_MASTER_DS9490) += ds9490r.o -ds9490r-objs := dscore.o - -obj-$(CONFIG_W1_MASTER_DS9490_BRIDGE) += ds_w1_bridge.o - +obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index d1cacd23576..af492cc48db 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -125,7 +125,7 @@ struct ds2482_w1_chan { struct ds2482_data { struct i2c_client client; - struct semaphore access_lock; + struct mutex access_lock; /* 1-wire interface(s) */ int w1_count; /* 1 or 8 */ @@ -265,7 +265,7 @@ static u8 ds2482_w1_touch_bit(void *data, u8 bit) struct ds2482_data *pdev = pchan->pdev; int status = -1; - down(&pdev->access_lock); + mutex_lock(&pdev->access_lock); /* Select the channel */ ds2482_wait_1wire_idle(pdev); @@ -277,7 +277,7 @@ static u8 ds2482_w1_touch_bit(void *data, u8 bit) bit ? 0xFF : 0)) status = ds2482_wait_1wire_idle(pdev); - up(&pdev->access_lock); + mutex_unlock(&pdev->access_lock); return (status & DS2482_REG_STS_SBR) ? 1 : 0; } @@ -297,7 +297,7 @@ static u8 ds2482_w1_triplet(void *data, u8 dbit) struct ds2482_data *pdev = pchan->pdev; int status = (3 << 5); - down(&pdev->access_lock); + mutex_lock(&pdev->access_lock); /* Select the channel */ ds2482_wait_1wire_idle(pdev); @@ -309,7 +309,7 @@ static u8 ds2482_w1_triplet(void *data, u8 dbit) dbit ? 0xFF : 0)) status = ds2482_wait_1wire_idle(pdev); - up(&pdev->access_lock); + mutex_unlock(&pdev->access_lock); /* Decode the status */ return (status >> 5); @@ -326,7 +326,7 @@ static void ds2482_w1_write_byte(void *data, u8 byte) struct ds2482_w1_chan *pchan = data; struct ds2482_data *pdev = pchan->pdev; - down(&pdev->access_lock); + mutex_lock(&pdev->access_lock); /* Select the channel */ ds2482_wait_1wire_idle(pdev); @@ -336,7 +336,7 @@ static void ds2482_w1_write_byte(void *data, u8 byte) /* Send the write byte command */ ds2482_send_cmd_data(pdev, DS2482_CMD_1WIRE_WRITE_BYTE, byte); - up(&pdev->access_lock); + mutex_unlock(&pdev->access_lock); } /** @@ -351,7 +351,7 @@ static u8 ds2482_w1_read_byte(void *data) struct ds2482_data *pdev = pchan->pdev; int result; - down(&pdev->access_lock); + mutex_lock(&pdev->access_lock); /* Select the channel */ ds2482_wait_1wire_idle(pdev); @@ -370,7 +370,7 @@ static u8 ds2482_w1_read_byte(void *data) /* Read the data byte */ result = i2c_smbus_read_byte(&pdev->client); - up(&pdev->access_lock); + mutex_unlock(&pdev->access_lock); return result; } @@ -389,7 +389,7 @@ static u8 ds2482_w1_reset_bus(void *data) int err; u8 retval = 1; - down(&pdev->access_lock); + mutex_lock(&pdev->access_lock); /* Select the channel */ ds2482_wait_1wire_idle(pdev); @@ -409,7 +409,7 @@ static u8 ds2482_w1_reset_bus(void *data) 0xF0); } - up(&pdev->access_lock); + mutex_unlock(&pdev->access_lock); return retval; } @@ -482,7 +482,7 @@ static int ds2482_detect(struct i2c_adapter *adapter, int address, int kind) snprintf(new_client->name, sizeof(new_client->name), "ds2482-%d00", data->w1_count); - init_MUTEX(&data->access_lock); + mutex_init(&data->access_lock); /* Tell the I2C layer a new client has arrived */ if ((err = i2c_attach_client(new_client))) diff --git a/drivers/w1/masters/dscore.c b/drivers/w1/masters/ds2490.c index 2cf7776a708..299e274d241 100644 --- a/drivers/w1/masters/dscore.c +++ b/drivers/w1/masters/ds2490.c @@ -24,7 +24,136 @@ #include <linux/mod_devicetable.h> #include <linux/usb.h> -#include "dscore.h" +#include "../w1_int.h" +#include "../w1.h" + +/* COMMAND TYPE CODES */ +#define CONTROL_CMD 0x00 +#define COMM_CMD 0x01 +#define MODE_CMD 0x02 + +/* CONTROL COMMAND CODES */ +#define CTL_RESET_DEVICE 0x0000 +#define CTL_START_EXE 0x0001 +#define CTL_RESUME_EXE 0x0002 +#define CTL_HALT_EXE_IDLE 0x0003 +#define CTL_HALT_EXE_DONE 0x0004 +#define CTL_FLUSH_COMM_CMDS 0x0007 +#define CTL_FLUSH_RCV_BUFFER 0x0008 +#define CTL_FLUSH_XMT_BUFFER 0x0009 +#define CTL_GET_COMM_CMDS 0x000A + +/* MODE COMMAND CODES */ +#define MOD_PULSE_EN 0x0000 +#define MOD_SPEED_CHANGE_EN 0x0001 +#define MOD_1WIRE_SPEED 0x0002 +#define MOD_STRONG_PU_DURATION 0x0003 +#define MOD_PULLDOWN_SLEWRATE 0x0004 +#define MOD_PROG_PULSE_DURATION 0x0005 +#define MOD_WRITE1_LOWTIME 0x0006 +#define MOD_DSOW0_TREC 0x0007 + +/* COMMUNICATION COMMAND CODES */ +#define COMM_ERROR_ESCAPE 0x0601 +#define COMM_SET_DURATION 0x0012 +#define COMM_BIT_IO 0x0020 +#define COMM_PULSE 0x0030 +#define COMM_1_WIRE_RESET 0x0042 +#define COMM_BYTE_IO 0x0052 +#define COMM_MATCH_ACCESS 0x0064 +#define COMM_BLOCK_IO 0x0074 +#define COMM_READ_STRAIGHT 0x0080 +#define COMM_DO_RELEASE 0x6092 +#define COMM_SET_PATH 0x00A2 +#define COMM_WRITE_SRAM_PAGE 0x00B2 +#define COMM_WRITE_EPROM 0x00C4 +#define COMM_READ_CRC_PROT_PAGE 0x00D4 +#define COMM_READ_REDIRECT_PAGE_CRC 0x21E4 +#define COMM_SEARCH_ACCESS 0x00F4 + +/* Communication command bits */ +#define COMM_TYPE 0x0008 +#define COMM_SE 0x0008 +#define COMM_D 0x0008 +#define COMM_Z 0x0008 +#define COMM_CH 0x0008 +#define COMM_SM 0x0008 +#define COMM_R 0x0008 +#define COMM_IM 0x0001 + +#define COMM_PS 0x4000 +#define COMM_PST 0x4000 +#define COMM_CIB 0x4000 +#define COMM_RTS 0x4000 +#define COMM_DT 0x2000 +#define COMM_SPU 0x1000 +#define COMM_F 0x0800 +#define COMM_NTP 0x0400 +#define COMM_ICP 0x0200 +#define COMM_RST 0x0100 + +#define PULSE_PROG 0x01 +#define PULSE_SPUE 0x02 + +#define BRANCH_MAIN 0xCC +#define BRANCH_AUX 0x33 + +/* + * Duration of the strong pull-up pulse in milliseconds. + */ +#define PULLUP_PULSE_DURATION 750 + +/* Status flags */ +#define ST_SPUA 0x01 /* Strong Pull-up is active */ +#define ST_PRGA 0x02 /* 12V programming pulse is being generated */ +#define ST_12VP 0x04 /* external 12V programming voltage is present */ +#define ST_PMOD 0x08 /* DS2490 powered from USB and external sources */ +#define ST_HALT 0x10 /* DS2490 is currently halted */ +#define ST_IDLE 0x20 /* DS2490 is currently idle */ +#define ST_EPOF 0x80 + +#define SPEED_NORMAL 0x00 +#define SPEED_FLEXIBLE 0x01 +#define SPEED_OVERDRIVE 0x02 + +#define NUM_EP 4 +#define EP_CONTROL 0 +#define EP_STATUS 1 +#define EP_DATA_OUT 2 +#define EP_DATA_IN 3 + +struct ds_device +{ + struct list_head ds_entry; + + struct usb_device *udev; + struct usb_interface *intf; + + int ep[NUM_EP]; + + struct w1_bus_master master; +}; + +struct ds_status +{ + u8 enable; + u8 speed; + u8 pullup_dur; + u8 ppuls_dur; + u8 pulldown_slew; + u8 write1_time; + u8 write0_time; + u8 reserved0; + u8 status; + u8 command0; + u8 command1; + u8 command_buffer_status; + u8 data_out_buffer_status; + u8 data_in_buffer_status; + u8 reserved1; + u8 reserved2; + +}; static struct usb_device_id ds_id_table [] = { { USB_DEVICE(0x04fa, 0x2490) }, @@ -35,21 +164,12 @@ MODULE_DEVICE_TABLE(usb, ds_id_table); static int ds_probe(struct usb_interface *, const struct usb_device_id *); static void ds_disconnect(struct usb_interface *); -int ds_touch_bit(struct ds_device *, u8, u8 *); -int ds_read_byte(struct ds_device *, u8 *); -int ds_read_bit(struct ds_device *, u8 *); -int ds_write_byte(struct ds_device *, u8); -int ds_write_bit(struct ds_device *, u8); -static int ds_start_pulse(struct ds_device *, int); -int ds_reset(struct ds_device *, struct ds_status *); -struct ds_device * ds_get_device(void); -void ds_put_device(struct ds_device *); - static inline void ds_dump_status(unsigned char *, unsigned char *, int); static int ds_send_control(struct ds_device *, u16, u16); -static int ds_send_control_mode(struct ds_device *, u16, u16); static int ds_send_control_cmd(struct ds_device *, u16, u16); +static LIST_HEAD(ds_devices); +static DEFINE_MUTEX(ds_mutex); static struct usb_driver ds_driver = { .name = "DS9490R", @@ -58,20 +178,6 @@ static struct usb_driver ds_driver = { .id_table = ds_id_table, }; -static struct ds_device *ds_dev; - -struct ds_device * ds_get_device(void) -{ - if (ds_dev) - atomic_inc(&ds_dev->refcnt); - return ds_dev; -} - -void ds_put_device(struct ds_device *dev) -{ - atomic_dec(&dev->refcnt); -} - static int ds_send_control_cmd(struct ds_device *dev, u16 value, u16 index) { int err; @@ -86,7 +192,7 @@ static int ds_send_control_cmd(struct ds_device *dev, u16 value, u16 index) return err; } - +#if 0 static int ds_send_control_mode(struct ds_device *dev, u16 value, u16 index) { int err; @@ -101,7 +207,7 @@ static int ds_send_control_mode(struct ds_device *dev, u16 value, u16 index) return err; } - +#endif static int ds_send_control(struct ds_device *dev, u16 value, u16 index) { int err; @@ -324,7 +430,7 @@ static int ds_wait_status(struct ds_device *dev, struct ds_status *st) return 0; } -int ds_reset(struct ds_device *dev, struct ds_status *st) +static int ds_reset(struct ds_device *dev, struct ds_status *st) { int err; @@ -345,7 +451,7 @@ int ds_reset(struct ds_device *dev, struct ds_status *st) } #if 0 -int ds_set_speed(struct ds_device *dev, int speed) +static int ds_set_speed(struct ds_device *dev, int speed) { int err; @@ -395,7 +501,7 @@ static int ds_start_pulse(struct ds_device *dev, int delay) return err; } -int ds_touch_bit(struct ds_device *dev, u8 bit, u8 *tbit) +static int ds_touch_bit(struct ds_device *dev, u8 bit, u8 *tbit) { int err, count; struct ds_status st; @@ -427,7 +533,7 @@ int ds_touch_bit(struct ds_device *dev, u8 bit, u8 *tbit) return 0; } -int ds_write_bit(struct ds_device *dev, u8 bit) +static int ds_write_bit(struct ds_device *dev, u8 bit) { int err; struct ds_status st; @@ -441,7 +547,7 @@ int ds_write_bit(struct ds_device *dev, u8 bit) return 0; } -int ds_write_byte(struct ds_device *dev, u8 byte) +static int ds_write_byte(struct ds_device *dev, u8 byte) { int err; struct ds_status st; @@ -464,26 +570,7 @@ int ds_write_byte(struct ds_device *dev, u8 byte) return !(byte == rbyte); } -int ds_read_bit(struct ds_device *dev, u8 *bit) -{ - int err; - - err = ds_send_control_mode(dev, MOD_PULSE_EN, PULSE_SPUE); - if (err) - return err; - - err = ds_send_control(dev, COMM_BIT_IO | COMM_IM | COMM_SPU | COMM_D, 0); - if (err) - return err; - - err = ds_recv_data(dev, bit, sizeof(*bit)); - if (err < 0) - return err; - - return 0; -} - -int ds_read_byte(struct ds_device *dev, u8 *byte) +static int ds_read_byte(struct ds_device *dev, u8 *byte) { int err; struct ds_status st; @@ -501,7 +588,7 @@ int ds_read_byte(struct ds_device *dev, u8 *byte) return 0; } -int ds_read_block(struct ds_device *dev, u8 *buf, int len) +static int ds_read_block(struct ds_device *dev, u8 *buf, int len) { struct ds_status st; int err; @@ -527,7 +614,7 @@ int ds_read_block(struct ds_device *dev, u8 *buf, int len) return err; } -int ds_write_block(struct ds_device *dev, u8 *buf, int len) +static int ds_write_block(struct ds_device *dev, u8 *buf, int len) { int err; struct ds_status st; @@ -555,7 +642,7 @@ int ds_write_block(struct ds_device *dev, u8 *buf, int len) #if 0 -int ds_search(struct ds_device *dev, u64 init, u64 *buf, u8 id_number, int conditional_search) +static int ds_search(struct ds_device *dev, u64 init, u64 *buf, u8 id_number, int conditional_search) { int err; u16 value, index; @@ -584,7 +671,7 @@ int ds_search(struct ds_device *dev, u64 init, u64 *buf, u8 id_number, int condi return err/8; } -int ds_match_access(struct ds_device *dev, u64 init) +static int ds_match_access(struct ds_device *dev, u64 init) { int err; struct ds_status st; @@ -604,7 +691,7 @@ int ds_match_access(struct ds_device *dev, u64 init) return 0; } -int ds_set_path(struct ds_device *dev, u64 init) +static int ds_set_path(struct ds_device *dev, u64 init) { int err; struct ds_status st; @@ -630,45 +717,156 @@ int ds_set_path(struct ds_device *dev, u64 init) #endif /* 0 */ +static u8 ds9490r_touch_bit(void *data, u8 bit) +{ + u8 ret; + struct ds_device *dev = data; + + if (ds_touch_bit(dev, bit, &ret)) + return 0; + + return ret; +} + +static void ds9490r_write_bit(void *data, u8 bit) +{ + struct ds_device *dev = data; + + ds_write_bit(dev, bit); +} + +static void ds9490r_write_byte(void *data, u8 byte) +{ + struct ds_device *dev = data; + + ds_write_byte(dev, byte); +} + +static u8 ds9490r_read_bit(void *data) +{ + struct ds_device *dev = data; + int err; + u8 bit = 0; + + err = ds_touch_bit(dev, 1, &bit); + if (err) + return 0; + + return bit & 1; +} + +static u8 ds9490r_read_byte(void *data) +{ + struct ds_device *dev = data; + int err; + u8 byte = 0; + + err = ds_read_byte(dev, &byte); + if (err) + return 0; + + return byte; +} + +static void ds9490r_write_block(void *data, const u8 *buf, int len) +{ + struct ds_device *dev = data; + + ds_write_block(dev, (u8 *)buf, len); +} + +static u8 ds9490r_read_block(void *data, u8 *buf, int len) +{ + struct ds_device *dev = data; + int err; + + err = ds_read_block(dev, buf, len); + if (err < 0) + return 0; + + return len; +} + +static u8 ds9490r_reset(void *data) +{ + struct ds_device *dev = data; + struct ds_status st; + int err; + + memset(&st, 0, sizeof(st)); + + err = ds_reset(dev, &st); + if (err) + return 1; + + return 0; +} + +static int ds_w1_init(struct ds_device *dev) +{ + memset(&dev->master, 0, sizeof(struct w1_bus_master)); + + dev->master.data = dev; + dev->master.touch_bit = &ds9490r_touch_bit; + dev->master.read_bit = &ds9490r_read_bit; + dev->master.write_bit = &ds9490r_write_bit; + dev->master.read_byte = &ds9490r_read_byte; + dev->master.write_byte = &ds9490r_write_byte; + dev->master.read_block = &ds9490r_read_block; + dev->master.write_block = &ds9490r_write_block; + dev->master.reset_bus = &ds9490r_reset; + + return w1_add_master_device(&dev->master); +} + +static void ds_w1_fini(struct ds_device *dev) +{ + w1_remove_master_device(&dev->master); +} + static int ds_probe(struct usb_interface *intf, const struct usb_device_id *udev_id) { struct usb_device *udev = interface_to_usbdev(intf); struct usb_endpoint_descriptor *endpoint; struct usb_host_interface *iface_desc; + struct ds_device *dev; int i, err; - ds_dev = kmalloc(sizeof(struct ds_device), GFP_KERNEL); - if (!ds_dev) { + dev = kmalloc(sizeof(struct ds_device), GFP_KERNEL); + if (!dev) { printk(KERN_INFO "Failed to allocate new DS9490R structure.\n"); return -ENOMEM; } + dev->udev = usb_get_dev(udev); + if (!dev->udev) { + err = -ENOMEM; + goto err_out_free; + } + memset(dev->ep, 0, sizeof(dev->ep)); - ds_dev->udev = usb_get_dev(udev); - usb_set_intfdata(intf, ds_dev); + usb_set_intfdata(intf, dev); - err = usb_set_interface(ds_dev->udev, intf->altsetting[0].desc.bInterfaceNumber, 3); + err = usb_set_interface(dev->udev, intf->altsetting[0].desc.bInterfaceNumber, 3); if (err) { printk(KERN_ERR "Failed to set alternative setting 3 for %d interface: err=%d.\n", intf->altsetting[0].desc.bInterfaceNumber, err); - return err; + goto err_out_clear; } - err = usb_reset_configuration(ds_dev->udev); + err = usb_reset_configuration(dev->udev); if (err) { printk(KERN_ERR "Failed to reset configuration: err=%d.\n", err); - return err; + goto err_out_clear; } iface_desc = &intf->altsetting[0]; if (iface_desc->desc.bNumEndpoints != NUM_EP-1) { printk(KERN_INFO "Num endpoints=%d. It is not DS9490R.\n", iface_desc->desc.bNumEndpoints); - return -ENODEV; + err = -EINVAL; + goto err_out_clear; } - atomic_set(&ds_dev->refcnt, 0); - memset(ds_dev->ep, 0, sizeof(ds_dev->ep)); - /* * This loop doesn'd show control 0 endpoint, * so we will fill only 1-3 endpoints entry. @@ -676,54 +874,31 @@ static int ds_probe(struct usb_interface *intf, for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - ds_dev->ep[i+1] = endpoint->bEndpointAddress; - + dev->ep[i+1] = endpoint->bEndpointAddress; +#if 0 printk("%d: addr=%x, size=%d, dir=%s, type=%x\n", i, endpoint->bEndpointAddress, le16_to_cpu(endpoint->wMaxPacketSize), (endpoint->bEndpointAddress & USB_DIR_IN)?"IN":"OUT", endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); +#endif } -#if 0 - { - int err, i; - u64 buf[3]; - u64 init=0xb30000002078ee81ull; - struct ds_status st; - - ds_reset(ds_dev, &st); - err = ds_search(ds_dev, init, buf, 3, 0); - if (err < 0) - return err; - for (i=0; i<err; ++i) - printk("%d: %llx\n", i, buf[i]); - - printk("Resetting...\n"); - ds_reset(ds_dev, &st); - printk("Setting path for %llx.\n", init); - err = ds_set_path(ds_dev, init); - if (err) - return err; - printk("Calling MATCH_ACCESS.\n"); - err = ds_match_access(ds_dev, init); - if (err) - return err; - - printk("Searching the bus...\n"); - err = ds_search(ds_dev, init, buf, 3, 0); - - printk("ds_search() returned %d\n", err); - - if (err < 0) - return err; - for (i=0; i<err; ++i) - printk("%d: %llx\n", i, buf[i]); + err = ds_w1_init(dev); + if (err) + goto err_out_clear; - return 0; - } -#endif + mutex_lock(&ds_mutex); + list_add_tail(&dev->ds_entry, &ds_devices); + mutex_unlock(&ds_mutex); return 0; + +err_out_clear: + usb_set_intfdata(intf, NULL); + usb_put_dev(dev->udev); +err_out_free: + kfree(dev); + return err; } static void ds_disconnect(struct usb_interface *intf) @@ -731,19 +906,19 @@ static void ds_disconnect(struct usb_interface *intf) struct ds_device *dev; dev = usb_get_intfdata(intf); - usb_set_intfdata(intf, NULL); + if (!dev) + return; - while (atomic_read(&dev->refcnt)) { - printk(KERN_INFO "Waiting for DS to become free: refcnt=%d.\n", - atomic_read(&dev->refcnt)); + mutex_lock(&ds_mutex); + list_del(&dev->ds_entry); + mutex_unlock(&ds_mutex); - if (msleep_interruptible(1000)) - flush_signals(current); - } + ds_w1_fini(dev); + + usb_set_intfdata(intf, NULL); usb_put_dev(dev->udev); kfree(dev); - ds_dev = NULL; } static int ds_init(void) @@ -769,27 +944,4 @@ module_exit(ds_fini); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>"); - -EXPORT_SYMBOL(ds_touch_bit); -EXPORT_SYMBOL(ds_read_byte); -EXPORT_SYMBOL(ds_read_bit); -EXPORT_SYMBOL(ds_read_block); -EXPORT_SYMBOL(ds_write_byte); -EXPORT_SYMBOL(ds_write_bit); -EXPORT_SYMBOL(ds_write_block); -EXPORT_SYMBOL(ds_reset); -EXPORT_SYMBOL(ds_get_device); -EXPORT_SYMBOL(ds_put_device); - -/* - * This functions can be used for EEPROM programming, - * when driver will be included into mainline this will - * require uncommenting. - */ -#if 0 -EXPORT_SYMBOL(ds_start_pulse); -EXPORT_SYMBOL(ds_set_speed); -EXPORT_SYMBOL(ds_detect); -EXPORT_SYMBOL(ds_stop_pulse); -EXPORT_SYMBOL(ds_search); -#endif +MODULE_DESCRIPTION("DS2490 USB <-> W1 bus master driver (DS9490*)"); diff --git a/drivers/w1/masters/ds_w1_bridge.c b/drivers/w1/masters/ds_w1_bridge.c deleted file mode 100644 index 5d30783a3eb..00000000000 --- a/drivers/w1/masters/ds_w1_bridge.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * ds_w1_bridge.c - * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> - * - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/module.h> -#include <linux/types.h> - -#include "../w1.h" -#include "../w1_int.h" -#include "dscore.h" - -static struct ds_device *ds_dev; -static struct w1_bus_master *ds_bus_master; - -static u8 ds9490r_touch_bit(void *data, u8 bit) -{ - u8 ret; - struct ds_device *dev = data; - - if (ds_touch_bit(dev, bit, &ret)) - return 0; - - return ret; -} - -static void ds9490r_write_bit(void *data, u8 bit) -{ - struct ds_device *dev = data; - - ds_write_bit(dev, bit); -} - -static void ds9490r_write_byte(void *data, u8 byte) -{ - struct ds_device *dev = data; - - ds_write_byte(dev, byte); -} - -static u8 ds9490r_read_bit(void *data) -{ - struct ds_device *dev = data; - int err; - u8 bit = 0; - - err = ds_touch_bit(dev, 1, &bit); - if (err) - return 0; - //err = ds_read_bit(dev, &bit); - //if (err) - // return 0; - - return bit & 1; -} - -static u8 ds9490r_read_byte(void *data) -{ - struct ds_device *dev = data; - int err; - u8 byte = 0; - - err = ds_read_byte(dev, &byte); - if (err) - return 0; - - return byte; -} - -static void ds9490r_write_block(void *data, const u8 *buf, int len) -{ - struct ds_device *dev = data; - - ds_write_block(dev, (u8 *)buf, len); -} - -static u8 ds9490r_read_block(void *data, u8 *buf, int len) -{ - struct ds_device *dev = data; - int err; - - err = ds_read_block(dev, buf, len); - if (err < 0) - return 0; - - return len; -} - -static u8 ds9490r_reset(void *data) -{ - struct ds_device *dev = data; - struct ds_status st; - int err; - - memset(&st, 0, sizeof(st)); - - err = ds_reset(dev, &st); - if (err) - return 1; - - return 0; -} - -static int __devinit ds_w1_init(void) -{ - int err; - - ds_bus_master = kmalloc(sizeof(*ds_bus_master), GFP_KERNEL); - if (!ds_bus_master) { - printk(KERN_ERR "Failed to allocate DS9490R USB<->W1 bus_master structure.\n"); - return -ENOMEM; - } - - ds_dev = ds_get_device(); - if (!ds_dev) { - printk(KERN_ERR "DS9490R is not registered.\n"); - err = -ENODEV; - goto err_out_free_bus_master; - } - - memset(ds_bus_master, 0, sizeof(*ds_bus_master)); - - ds_bus_master->data = ds_dev; - ds_bus_master->touch_bit = &ds9490r_touch_bit; - ds_bus_master->read_bit = &ds9490r_read_bit; - ds_bus_master->write_bit = &ds9490r_write_bit; - ds_bus_master->read_byte = &ds9490r_read_byte; - ds_bus_master->write_byte = &ds9490r_write_byte; - ds_bus_master->read_block = &ds9490r_read_block; - ds_bus_master->write_block = &ds9490r_write_block; - ds_bus_master->reset_bus = &ds9490r_reset; - - err = w1_add_master_device(ds_bus_master); - if (err) - goto err_out_put_device; - - return 0; - -err_out_put_device: - ds_put_device(ds_dev); -err_out_free_bus_master: - kfree(ds_bus_master); - - return err; -} - -static void __devexit ds_w1_fini(void) -{ - w1_remove_master_device(ds_bus_master); - ds_put_device(ds_dev); - kfree(ds_bus_master); -} - -module_init(ds_w1_init); -module_exit(ds_w1_fini); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>"); diff --git a/drivers/w1/masters/dscore.h b/drivers/w1/masters/dscore.h deleted file mode 100644 index 6cf5671d6eb..00000000000 --- a/drivers/w1/masters/dscore.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * dscore.h - * - * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> - * - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __DSCORE_H -#define __DSCORE_H - -#include <linux/usb.h> -#include <asm/atomic.h> - -/* COMMAND TYPE CODES */ -#define CONTROL_CMD 0x00 -#define COMM_CMD 0x01 -#define MODE_CMD 0x02 - -/* CONTROL COMMAND CODES */ -#define CTL_RESET_DEVICE 0x0000 -#define CTL_START_EXE 0x0001 -#define CTL_RESUME_EXE 0x0002 -#define CTL_HALT_EXE_IDLE 0x0003 -#define CTL_HALT_EXE_DONE 0x0004 -#define CTL_FLUSH_COMM_CMDS 0x0007 -#define CTL_FLUSH_RCV_BUFFER 0x0008 -#define CTL_FLUSH_XMT_BUFFER 0x0009 -#define CTL_GET_COMM_CMDS 0x000A - -/* MODE COMMAND CODES */ -#define MOD_PULSE_EN 0x0000 -#define MOD_SPEED_CHANGE_EN 0x0001 -#define MOD_1WIRE_SPEED 0x0002 -#define MOD_STRONG_PU_DURATION 0x0003 -#define MOD_PULLDOWN_SLEWRATE 0x0004 -#define MOD_PROG_PULSE_DURATION 0x0005 -#define MOD_WRITE1_LOWTIME 0x0006 -#define MOD_DSOW0_TREC 0x0007 - -/* COMMUNICATION COMMAND CODES */ -#define COMM_ERROR_ESCAPE 0x0601 -#define COMM_SET_DURATION 0x0012 -#define COMM_BIT_IO 0x0020 -#define COMM_PULSE 0x0030 -#define COMM_1_WIRE_RESET 0x0042 -#define COMM_BYTE_IO 0x0052 -#define COMM_MATCH_ACCESS 0x0064 -#define COMM_BLOCK_IO 0x0074 -#define COMM_READ_STRAIGHT 0x0080 -#define COMM_DO_RELEASE 0x6092 -#define COMM_SET_PATH 0x00A2 -#define COMM_WRITE_SRAM_PAGE 0x00B2 -#define COMM_WRITE_EPROM 0x00C4 -#define COMM_READ_CRC_PROT_PAGE 0x00D4 -#define COMM_READ_REDIRECT_PAGE_CRC 0x21E4 -#define COMM_SEARCH_ACCESS 0x00F4 - -/* Communication command bits */ -#define COMM_TYPE 0x0008 -#define COMM_SE 0x0008 -#define COMM_D 0x0008 -#define COMM_Z 0x0008 -#define COMM_CH 0x0008 -#define COMM_SM 0x0008 -#define COMM_R 0x0008 -#define COMM_IM 0x0001 - -#define COMM_PS 0x4000 -#define COMM_PST 0x4000 -#define COMM_CIB 0x4000 -#define COMM_RTS 0x4000 -#define COMM_DT 0x2000 -#define COMM_SPU 0x1000 -#define COMM_F 0x0800 -#define COMM_NTP 0x0400 -#define COMM_ICP 0x0200 -#define COMM_RST 0x0100 - -#define PULSE_PROG 0x01 -#define PULSE_SPUE 0x02 - -#define BRANCH_MAIN 0xCC -#define BRANCH_AUX 0x33 - -/* - * Duration of the strong pull-up pulse in milliseconds. - */ -#define PULLUP_PULSE_DURATION 750 - -/* Status flags */ -#define ST_SPUA 0x01 /* Strong Pull-up is active */ -#define ST_PRGA 0x02 /* 12V programming pulse is being generated */ -#define ST_12VP 0x04 /* external 12V programming voltage is present */ -#define ST_PMOD 0x08 /* DS2490 powered from USB and external sources */ -#define ST_HALT 0x10 /* DS2490 is currently halted */ -#define ST_IDLE 0x20 /* DS2490 is currently idle */ -#define ST_EPOF 0x80 - -#define SPEED_NORMAL 0x00 -#define SPEED_FLEXIBLE 0x01 -#define SPEED_OVERDRIVE 0x02 - -#define NUM_EP 4 -#define EP_CONTROL 0 -#define EP_STATUS 1 -#define EP_DATA_OUT 2 -#define EP_DATA_IN 3 - -struct ds_device -{ - struct usb_device *udev; - struct usb_interface *intf; - - int ep[NUM_EP]; - - atomic_t refcnt; -}; - -struct ds_status -{ - u8 enable; - u8 speed; - u8 pullup_dur; - u8 ppuls_dur; - u8 pulldown_slew; - u8 write1_time; - u8 write0_time; - u8 reserved0; - u8 status; - u8 command0; - u8 command1; - u8 command_buffer_status; - u8 data_out_buffer_status; - u8 data_in_buffer_status; - u8 reserved1; - u8 reserved2; - -}; - -int ds_touch_bit(struct ds_device *, u8, u8 *); -int ds_read_byte(struct ds_device *, u8 *); -int ds_read_bit(struct ds_device *, u8 *); -int ds_write_byte(struct ds_device *, u8); -int ds_write_bit(struct ds_device *, u8); -int ds_reset(struct ds_device *, struct ds_status *); -struct ds_device * ds_get_device(void); -void ds_put_device(struct ds_device *); -int ds_write_block(struct ds_device *, u8 *, int); -int ds_read_block(struct ds_device *, u8 *, int); - -#endif /* __DSCORE_H */ - diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index f9d4c91fc53..d18d6424cd2 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -28,7 +28,7 @@ config W1_SLAVE_DS2433 config W1_SLAVE_DS2433_CRC bool "Protect DS2433 data with a CRC16" - depends on W1_DS2433 + depends on W1_SLAVE_DS2433 select CRC16 help Say Y here to protect DS2433 data with a CRC16. diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index fb118be789e..2ac238f1480 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -22,7 +22,6 @@ #endif #include "../w1.h" -#include "../w1_io.h" #include "../w1_int.h" #include "../w1_family.h" @@ -106,11 +105,7 @@ static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off, if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0) return 0; - atomic_inc(&sl->refcnt); - if (down_interruptible(&sl->master->mutex)) { - count = 0; - goto out_dec; - } + mutex_lock(&sl->master->mutex); #ifdef CONFIG_W1_F23_CRC @@ -141,9 +136,7 @@ static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off, #endif /* CONFIG_W1_F23_CRC */ out_up: - up(&sl->master->mutex); -out_dec: - atomic_dec(&sl->refcnt); + mutex_unlock(&sl->master->mutex); return count; } @@ -232,11 +225,7 @@ static ssize_t w1_f23_write_bin(struct kobject *kobj, char *buf, loff_t off, } #endif /* CONFIG_W1_F23_CRC */ - atomic_inc(&sl->refcnt); - if (down_interruptible(&sl->master->mutex)) { - count = 0; - goto out_dec; - } + mutex_lock(&sl->master->mutex); /* Can only write data to one page at a time */ idx = 0; @@ -254,9 +243,7 @@ static ssize_t w1_f23_write_bin(struct kobject *kobj, char *buf, loff_t off, } out_up: - up(&sl->master->mutex); -out_dec: - atomic_dec(&sl->refcnt); + mutex_unlock(&sl->master->mutex); return count; } diff --git a/drivers/w1/slaves/w1_smem.c b/drivers/w1/slaves/w1_smem.c index c6d3be54f94..cc8c02e9259 100644 --- a/drivers/w1/slaves/w1_smem.c +++ b/drivers/w1/slaves/w1_smem.c @@ -28,7 +28,6 @@ #include <linux/types.h> #include "../w1.h" -#include "../w1_io.h" #include "../w1_int.h" #include "../w1_family.h" diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 536d16d78de..5372cfcbd05 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -29,7 +29,6 @@ #include <linux/delay.h> #include "../w1.h" -#include "../w1_io.h" #include "../w1_int.h" #include "../w1_family.h" @@ -166,12 +165,7 @@ static ssize_t w1_therm_read_bin(struct kobject *kobj, char *buf, loff_t off, si u8 rom[9], crc, verdict; int i, max_trying = 10; - atomic_inc(&sl->refcnt); - smp_mb__after_atomic_inc(); - if (down_interruptible(&sl->master->mutex)) { - count = 0; - goto out_dec; - } + mutex_lock(&sl->master->mutex); if (off > W1_SLAVE_DATA_SIZE) { count = 0; @@ -234,10 +228,7 @@ static ssize_t w1_therm_read_bin(struct kobject *kobj, char *buf, loff_t off, si count += sprintf(buf + count, "t=%d\n", w1_convert_temp(rom, sl->family->fid)); out: - up(&dev->mutex); -out_dec: - smp_mb__before_atomic_inc(); - atomic_dec(&sl->refcnt); + mutex_unlock(&dev->mutex); return count; } diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index a698b517e86..de3e9791f80 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -35,7 +35,6 @@ #include <asm/atomic.h> #include "w1.h" -#include "w1_io.h" #include "w1_log.h" #include "w1_int.h" #include "w1_family.h" @@ -55,7 +54,7 @@ module_param_named(control_timeout, w1_control_timeout, int, 0); module_param_named(max_slave_count, w1_max_slave_count, int, 0); module_param_named(slave_ttl, w1_max_slave_ttl, int, 0); -DEFINE_SPINLOCK(w1_mlock); +DEFINE_MUTEX(w1_mlock); LIST_HEAD(w1_masters); static struct task_struct *w1_control_thread; @@ -75,8 +74,6 @@ static void w1_master_release(struct device *dev) struct w1_master *md = dev_to_w1_master(dev); dev_dbg(dev, "%s: Releasing %s.\n", __func__, md->name); - - dev_fini_netlink(md); memset(md, 0, sizeof(struct w1_master) + sizeof(struct w1_bus_master)); kfree(md); } @@ -85,10 +82,10 @@ static void w1_slave_release(struct device *dev) { struct w1_slave *sl = dev_to_w1_slave(dev); - dev_dbg(dev, "%s: Releasing %s.\n", __func__, sl->name); + printk("%s: Releasing %s.\n", __func__, sl->name); while (atomic_read(&sl->refcnt)) { - dev_dbg(dev, "Waiting for %s to become free: refcnt=%d.\n", + printk("Waiting for %s to become free: refcnt=%d.\n", sl->name, atomic_read(&sl->refcnt)); if (msleep_interruptible(1000)) flush_signals(current); @@ -111,7 +108,6 @@ static ssize_t w1_slave_read_id(struct kobject *kobj, char *buf, loff_t off, siz { struct w1_slave *sl = kobj_to_w1_slave(kobj); - atomic_inc(&sl->refcnt); if (off > 8) { count = 0; } else { @@ -120,7 +116,6 @@ static ssize_t w1_slave_read_id(struct kobject *kobj, char *buf, loff_t off, siz memcpy(buf, (u8 *)&sl->reg_num, count); } - atomic_dec(&sl->refcnt); return count; } @@ -139,7 +134,63 @@ static struct bin_attribute w1_slave_attr_bin_id = { }; /* Default family */ -static struct w1_family w1_default_family; + +static ssize_t w1_default_write(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + + mutex_lock(&sl->master->mutex); + if (w1_reset_select_slave(sl)) { + count = 0; + goto out_up; + } + + w1_write_block(sl->master, buf, count); + +out_up: + mutex_unlock(&sl->master->mutex); + return count; +} + +static ssize_t w1_default_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + + mutex_lock(&sl->master->mutex); + w1_read_block(sl->master, buf, count); + mutex_unlock(&sl->master->mutex); + return count; +} + +static struct bin_attribute w1_default_attr = { + .attr = { + .name = "rw", + .mode = S_IRUGO | S_IWUSR, + .owner = THIS_MODULE, + }, + .size = PAGE_SIZE, + .read = w1_default_read, + .write = w1_default_write, +}; + +static int w1_default_add_slave(struct w1_slave *sl) +{ + return sysfs_create_bin_file(&sl->dev.kobj, &w1_default_attr); +} + +static void w1_default_remove_slave(struct w1_slave *sl) +{ + sysfs_remove_bin_file(&sl->dev.kobj, &w1_default_attr); +} + +static struct w1_family_ops w1_default_fops = { + .add_slave = w1_default_add_slave, + .remove_slave = w1_default_remove_slave, +}; + +static struct w1_family w1_default_family = { + .fops = &w1_default_fops, +}; static int w1_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size); @@ -183,12 +234,9 @@ static ssize_t w1_master_attribute_show_name(struct device *dev, struct device_a struct w1_master *md = dev_to_w1_master(dev); ssize_t count; - if (down_interruptible (&md->mutex)) - return -EBUSY; - + mutex_lock(&md->mutex); count = sprintf(buf, "%s\n", md->name); - - up(&md->mutex); + mutex_unlock(&md->mutex); return count; } @@ -199,12 +247,9 @@ static ssize_t w1_master_attribute_store_search(struct device * dev, { struct w1_master *md = dev_to_w1_master(dev); - if (down_interruptible (&md->mutex)) - return -EBUSY; - + mutex_lock(&md->mutex); md->search_count = simple_strtol(buf, NULL, 0); - - up(&md->mutex); + mutex_unlock(&md->mutex); return count; } @@ -216,12 +261,9 @@ static ssize_t w1_master_attribute_show_search(struct device *dev, struct w1_master *md = dev_to_w1_master(dev); ssize_t count; - if (down_interruptible (&md->mutex)) - return -EBUSY; - + mutex_lock(&md->mutex); count = sprintf(buf, "%d\n", md->search_count); - - up(&md->mutex); + mutex_unlock(&md->mutex); return count; } @@ -231,12 +273,9 @@ static ssize_t w1_master_attribute_show_pointer(struct device *dev, struct devic struct w1_master *md = dev_to_w1_master(dev); ssize_t count; - if (down_interruptible(&md->mutex)) - return -EBUSY; - + mutex_lock(&md->mutex); count = sprintf(buf, "0x%p\n", md->bus_master); - - up(&md->mutex); + mutex_unlock(&md->mutex); return count; } @@ -252,12 +291,9 @@ static ssize_t w1_master_attribute_show_max_slave_count(struct device *dev, stru struct w1_master *md = dev_to_w1_master(dev); ssize_t count; - if (down_interruptible(&md->mutex)) - return -EBUSY; - + mutex_lock(&md->mutex); count = sprintf(buf, "%d\n", md->max_slave_count); - - up(&md->mutex); + mutex_unlock(&md->mutex); return count; } @@ -266,12 +302,9 @@ static ssize_t w1_master_attribute_show_attempts(struct device *dev, struct devi struct w1_master *md = dev_to_w1_master(dev); ssize_t count; - if (down_interruptible(&md->mutex)) - return -EBUSY; - + mutex_lock(&md->mutex); count = sprintf(buf, "%lu\n", md->attempts); - - up(&md->mutex); + mutex_unlock(&md->mutex); return count; } @@ -280,12 +313,9 @@ static ssize_t w1_master_attribute_show_slave_count(struct device *dev, struct d struct w1_master *md = dev_to_w1_master(dev); ssize_t count; - if (down_interruptible(&md->mutex)) - return -EBUSY; - + mutex_lock(&md->mutex); count = sprintf(buf, "%d\n", md->slave_count); - - up(&md->mutex); + mutex_unlock(&md->mutex); return count; } @@ -294,8 +324,7 @@ static ssize_t w1_master_attribute_show_slaves(struct device *dev, struct device struct w1_master *md = dev_to_w1_master(dev); int c = PAGE_SIZE; - if (down_interruptible(&md->mutex)) - return -EBUSY; + mutex_lock(&md->mutex); if (md->slave_count == 0) c -= snprintf(buf + PAGE_SIZE - c, c, "not found.\n"); @@ -310,7 +339,7 @@ static ssize_t w1_master_attribute_show_slaves(struct device *dev, struct device } } - up(&md->mutex); + mutex_unlock(&md->mutex); return PAGE_SIZE - c; } @@ -362,7 +391,8 @@ static void w1_destroy_master_attributes(struct w1_master *master) } #ifdef CONFIG_HOTPLUG -static int w1_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) +static int w1_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) { struct w1_master *md = NULL; struct w1_slave *sl = NULL; @@ -382,7 +412,8 @@ static int w1_uevent(struct device *dev, char **envp, int num_envp, char *buffer return -EINVAL; } - dev_dbg(dev, "Hotplug event for %s %s, bus_id=%s.\n", event_owner, name, dev->bus_id); + dev_dbg(dev, "Hotplug event for %s %s, bus_id=%s.\n", + event_owner, name, dev->bus_id); if (dev->driver != &w1_slave_driver || !sl) return 0; @@ -401,7 +432,8 @@ static int w1_uevent(struct device *dev, char **envp, int num_envp, char *buffer return 0; }; #else -static int w1_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) +static int w1_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) { return 0; } @@ -425,7 +457,8 @@ static int __w1_attach_slave_device(struct w1_slave *sl) (unsigned int) sl->reg_num.family, (unsigned long long) sl->reg_num.id); - dev_dbg(&sl->dev, "%s: registering %s as %p.\n", __func__, &sl->dev.bus_id[0]); + dev_dbg(&sl->dev, "%s: registering %s as %p.\n", __func__, + &sl->dev.bus_id[0]); err = device_register(&sl->dev); if (err < 0) { @@ -496,6 +529,7 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) sl->master = dev; set_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags); + memset(&msg, 0, sizeof(msg)); memcpy(&sl->reg_num, rn, sizeof(sl->reg_num)); atomic_set(&sl->refcnt, 0); init_completion(&sl->released); @@ -526,7 +560,7 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) sl->ttl = dev->slave_ttl; dev->slave_count++; - memcpy(&msg.id.id, rn, sizeof(msg.id.id)); + memcpy(msg.id.id, rn, sizeof(msg.id)); msg.type = W1_SLAVE_ADD; w1_netlink_send(dev, &msg); @@ -544,7 +578,8 @@ static void w1_slave_detach(struct w1_slave *sl) if (sl->family->fops && sl->family->fops->remove_slave) sl->family->fops->remove_slave(sl); - memcpy(&msg.id.id, &sl->reg_num, sizeof(msg.id.id)); + memset(&msg, 0, sizeof(msg)); + memcpy(msg.id.id, &sl->reg_num, sizeof(msg.id)); msg.type = W1_SLAVE_REMOVE; w1_netlink_send(sl->master, &msg); @@ -561,7 +596,7 @@ static struct w1_master *w1_search_master(void *data) struct w1_master *dev; int found = 0; - spin_lock_bh(&w1_mlock); + mutex_lock(&w1_mlock); list_for_each_entry(dev, &w1_masters, w1_master_entry) { if (dev->bus_master->data == data) { found = 1; @@ -569,22 +604,69 @@ static struct w1_master *w1_search_master(void *data) break; } } - spin_unlock_bh(&w1_mlock); + mutex_unlock(&w1_mlock); + + return (found)?dev:NULL; +} + +struct w1_master *w1_search_master_id(u32 id) +{ + struct w1_master *dev; + int found = 0; + + mutex_lock(&w1_mlock); + list_for_each_entry(dev, &w1_masters, w1_master_entry) { + if (dev->id == id) { + found = 1; + atomic_inc(&dev->refcnt); + break; + } + } + mutex_unlock(&w1_mlock); return (found)?dev:NULL; } +struct w1_slave *w1_search_slave(struct w1_reg_num *id) +{ + struct w1_master *dev; + struct w1_slave *sl = NULL; + int found = 0; + + mutex_lock(&w1_mlock); + list_for_each_entry(dev, &w1_masters, w1_master_entry) { + mutex_lock(&dev->mutex); + list_for_each_entry(sl, &dev->slist, w1_slave_entry) { + if (sl->reg_num.family == id->family && + sl->reg_num.id == id->id && + sl->reg_num.crc == id->crc) { + found = 1; + atomic_inc(&dev->refcnt); + atomic_inc(&sl->refcnt); + break; + } + } + mutex_unlock(&dev->mutex); + + if (found) + break; + } + mutex_unlock(&w1_mlock); + + return (found)?sl:NULL; +} + void w1_reconnect_slaves(struct w1_family *f) { struct w1_master *dev; - spin_lock_bh(&w1_mlock); + mutex_lock(&w1_mlock); list_for_each_entry(dev, &w1_masters, w1_master_entry) { dev_dbg(&dev->dev, "Reconnecting slaves in %s into new family %02x.\n", dev->name, f->fid); set_bit(W1_MASTER_NEED_RECONNECT, &dev->flags); } - spin_unlock_bh(&w1_mlock); + mutex_unlock(&w1_mlock); } static void w1_slave_found(void *data, u64 rn) @@ -646,7 +728,7 @@ static void w1_slave_found(void *data, u64 rn) * @dev The master device to search * @cb Function to call when a device is found */ -void w1_search(struct w1_master *dev, w1_slave_found_callback cb) +void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb) { u64 last_rn, rn, tmp64; int i, slave_count = 0; @@ -677,7 +759,7 @@ void w1_search(struct w1_master *dev, w1_slave_found_callback cb) } /* Start the search */ - w1_write_8(dev, W1_SEARCH); + w1_write_8(dev, search_type); for (i = 0; i < 64; ++i) { /* Determine the direction/search bit */ if (i == desc_bit) @@ -739,23 +821,23 @@ static int w1_control(void *data) if (kthread_should_stop() || test_bit(W1_MASTER_NEED_EXIT, &dev->flags)) { set_bit(W1_MASTER_NEED_EXIT, &dev->flags); - spin_lock(&w1_mlock); + mutex_lock(&w1_mlock); list_del(&dev->w1_master_entry); - spin_unlock(&w1_mlock); + mutex_unlock(&w1_mlock); - down(&dev->mutex); + mutex_lock(&dev->mutex); list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { w1_slave_detach(sl); } w1_destroy_master_attributes(dev); - up(&dev->mutex); + mutex_unlock(&dev->mutex); atomic_dec(&dev->refcnt); continue; } if (test_bit(W1_MASTER_NEED_RECONNECT, &dev->flags)) { dev_dbg(&dev->dev, "Reconnecting slaves in device %s.\n", dev->name); - down(&dev->mutex); + mutex_lock(&dev->mutex); list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { if (sl->family->fid == W1_FAMILY_DEFAULT) { struct w1_reg_num rn; @@ -768,7 +850,7 @@ static int w1_control(void *data) } dev_dbg(&dev->dev, "Reconnecting slaves in device %s has been finished.\n", dev->name); clear_bit(W1_MASTER_NEED_RECONNECT, &dev->flags); - up(&dev->mutex); + mutex_unlock(&dev->mutex); } } } @@ -776,10 +858,31 @@ static int w1_control(void *data) return 0; } +void w1_search_process(struct w1_master *dev, u8 search_type) +{ + struct w1_slave *sl, *sln; + + list_for_each_entry(sl, &dev->slist, w1_slave_entry) + clear_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags); + + w1_search_devices(dev, search_type, w1_slave_found); + + list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { + if (!test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags) && !--sl->ttl) { + w1_slave_detach(sl); + + dev->slave_count--; + } else if (test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags)) + sl->ttl = dev->slave_ttl; + } + + if (dev->search_count > 0) + dev->search_count--; +} + int w1_process(void *data) { struct w1_master *dev = (struct w1_master *) data; - struct w1_slave *sl, *sln; while (!kthread_should_stop() && !test_bit(W1_MASTER_NEED_EXIT, &dev->flags)) { try_to_freeze(); @@ -794,27 +897,9 @@ int w1_process(void *data) if (dev->search_count == 0) continue; - if (down_interruptible(&dev->mutex)) - continue; - - list_for_each_entry(sl, &dev->slist, w1_slave_entry) - clear_bit(W1_SLAVE_ACTIVE, (long *)&sl->flags); - - w1_search_devices(dev, w1_slave_found); - - list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) { - if (!test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags) && !--sl->ttl) { - w1_slave_detach(sl); - - dev->slave_count--; - } else if (test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags)) - sl->ttl = dev->slave_ttl; - } - - if (dev->search_count > 0) - dev->search_count--; - - up(&dev->mutex); + mutex_lock(&dev->mutex); + w1_search_process(dev, W1_SEARCH); + mutex_unlock(&dev->mutex); } atomic_dec(&dev->refcnt); @@ -828,6 +913,8 @@ static int w1_init(void) printk(KERN_INFO "Driver for 1-wire Dallas network protocol.\n"); + w1_init_netlink(); + retval = bus_register(&w1_bus_type); if (retval) { printk(KERN_ERR "Failed to register bus. err=%d.\n", retval); @@ -880,6 +967,8 @@ static void w1_fini(void) list_for_each_entry(dev, &w1_masters, w1_master_entry) __w1_remove_master_device(dev); + w1_fini_netlink(); + kthread_stop(w1_control_thread); driver_unregister(&w1_slave_driver); diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index 56980505e6c..f1df5343f4a 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h @@ -41,10 +41,7 @@ struct w1_reg_num #include <linux/completion.h> #include <linux/device.h> - -#include <net/sock.h> - -#include <asm/semaphore.h> +#include <linux/mutex.h> #include "w1_family.h" @@ -52,7 +49,7 @@ struct w1_reg_num #define W1_SLAVE_DATA_SIZE 128 #define W1_SEARCH 0xF0 -#define W1_CONDITIONAL_SEARCH 0xEC +#define W1_ALARM_SEARCH 0xEC #define W1_CONVERT_TEMP 0x44 #define W1_SKIP_ROM 0xCC #define W1_READ_SCRATCHPAD 0xBE @@ -60,7 +57,7 @@ struct w1_reg_num #define W1_READ_PSUPPLY 0xB4 #define W1_MATCH_ROM 0x55 -#define W1_SLAVE_ACTIVE (1<<0) +#define W1_SLAVE_ACTIVE 0 struct w1_slave { @@ -145,8 +142,8 @@ struct w1_bus_master */ u8 (*reset_bus)(void *); - /** Really nice hardware can handles the ROM searches */ - void (*search)(void *, w1_slave_found_callback); + /** Really nice hardware can handles the different types of ROM search */ + void (*search)(void *, u8, w1_slave_found_callback); }; #define W1_MASTER_NEED_EXIT 0 @@ -173,19 +170,30 @@ struct w1_master long flags; struct task_struct *thread; - struct semaphore mutex; + struct mutex mutex; struct device_driver *driver; struct device dev; struct w1_bus_master *bus_master; - u32 seq, groups; - struct sock *nls; + u32 seq; }; int w1_create_master_attributes(struct w1_master *); -void w1_search(struct w1_master *dev, w1_slave_found_callback cb); +void w1_search(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); +void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb); +struct w1_slave *w1_search_slave(struct w1_reg_num *id); +void w1_search_process(struct w1_master *dev, u8 search_type); +struct w1_master *w1_search_master_id(u32 id); + +u8 w1_triplet(struct w1_master *dev, int bdir); +void w1_write_8(struct w1_master *, u8); +int w1_reset_bus(struct w1_master *); +u8 w1_calc_crc8(u8 *, int); +void w1_write_block(struct w1_master *, const u8 *, int); +u8 w1_read_block(struct w1_master *, u8 *, int); +int w1_reset_select_slave(struct w1_slave *sl); static inline struct w1_slave* dev_to_w1_slave(struct device *dev) { @@ -202,15 +210,14 @@ static inline struct w1_master* dev_to_w1_master(struct device *dev) return container_of(dev, struct w1_master, dev); } +extern struct device_driver w1_master_driver; +extern struct device w1_master_device; extern int w1_max_slave_count; extern int w1_max_slave_ttl; -extern spinlock_t w1_mlock; extern struct list_head w1_masters; -extern struct device_driver w1_master_driver; -extern struct device w1_master_device; +extern struct mutex w1_mlock; -int w1_process(void *data); -void w1_reconnect_slaves(struct w1_family *f); +extern int w1_process(void *); #endif /* __KERNEL__ */ diff --git a/drivers/w1/w1_family.c b/drivers/w1/w1_family.c index 0e32c114f90..a3c95bd6890 100644 --- a/drivers/w1/w1_family.c +++ b/drivers/w1/w1_family.c @@ -107,6 +107,12 @@ struct w1_family * w1_family_registered(u8 fid) return (ret) ? f : NULL; } +static void __w1_family_put(struct w1_family *f) +{ + if (atomic_dec_and_test(&f->refcnt)) + f->need_exit = 1; +} + void w1_family_put(struct w1_family *f) { spin_lock(&w1_flock); @@ -114,19 +120,14 @@ void w1_family_put(struct w1_family *f) spin_unlock(&w1_flock); } -void __w1_family_put(struct w1_family *f) -{ - if (atomic_dec_and_test(&f->refcnt)) - f->need_exit = 1; -} - +#if 0 void w1_family_get(struct w1_family *f) { spin_lock(&w1_flock); __w1_family_get(f); spin_unlock(&w1_flock); - } +#endif /* 0 */ void __w1_family_get(struct w1_family *f) { @@ -135,8 +136,5 @@ void __w1_family_get(struct w1_family *f) smp_mb__after_atomic_inc(); } -EXPORT_SYMBOL(w1_family_get); -EXPORT_SYMBOL(w1_family_put); -EXPORT_SYMBOL(w1_family_registered); EXPORT_SYMBOL(w1_unregister_family); EXPORT_SYMBOL(w1_register_family); diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h index 2ca0489c716..1e2ac40c2c1 100644 --- a/drivers/w1/w1_family.h +++ b/drivers/w1/w1_family.h @@ -57,12 +57,11 @@ struct w1_family extern spinlock_t w1_flock; -void w1_family_get(struct w1_family *); void w1_family_put(struct w1_family *); void __w1_family_get(struct w1_family *); -void __w1_family_put(struct w1_family *); struct w1_family * w1_family_registered(u8); void w1_unregister_family(struct w1_family *); int w1_register_family(struct w1_family *); +void w1_reconnect_slaves(struct w1_family *f); #endif /* __W1_FAMILY_H */ diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c index 68565aacec7..357a2e0f637 100644 --- a/drivers/w1/w1_int.c +++ b/drivers/w1/w1_int.c @@ -65,7 +65,7 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl, atomic_set(&dev->refcnt, 2); INIT_LIST_HEAD(&dev->slist); - init_MUTEX(&dev->mutex); + mutex_init(&dev->mutex); memcpy(&dev->dev, device, sizeof(struct device)); snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id), @@ -74,16 +74,11 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl, dev->driver = driver; - dev->groups = 1; dev->seq = 1; - dev_init_netlink(dev); err = device_register(&dev->dev); if (err) { printk(KERN_ERR "Failed to register master device. err=%d\n", err); - - dev_fini_netlink(dev); - memset(dev, 0, sizeof(struct w1_master)); kfree(dev); dev = NULL; @@ -131,12 +126,12 @@ int w1_add_master_device(struct w1_bus_master *master) dev->initialized = 1; - spin_lock(&w1_mlock); + mutex_lock(&w1_mlock); list_add(&dev->w1_master_entry, &w1_masters); - spin_unlock(&w1_mlock); + mutex_unlock(&w1_mlock); + memset(&msg, 0, sizeof(msg)); msg.id.mst.id = dev->id; - msg.id.mst.pid = dev->thread->pid; msg.type = W1_MASTER_ADD; w1_netlink_send(dev, &msg); @@ -153,7 +148,6 @@ err_out_free_dev: void __w1_remove_master_device(struct w1_master *dev) { struct w1_netlink_msg msg; - pid_t pid = dev->thread->pid; set_bit(W1_MASTER_NEED_EXIT, &dev->flags); kthread_stop(dev->thread); @@ -166,8 +160,8 @@ void __w1_remove_master_device(struct w1_master *dev) flush_signals(current); } + memset(&msg, 0, sizeof(msg)); msg.id.mst.id = dev->id; - msg.id.mst.pid = pid; msg.type = W1_MASTER_REMOVE; w1_netlink_send(dev, &msg); diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index f7f7e8bec30..30b6fbf83bd 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -23,10 +23,10 @@ #include <linux/delay.h> #include <linux/moduleparam.h> +#include <linux/module.h> #include "w1.h" #include "w1_log.h" -#include "w1_io.h" static int w1_delay_parm = 1; module_param_named(delay_coef, w1_delay_parm, int, 0); @@ -50,7 +50,7 @@ static u8 w1_crc8_table[] = { 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53 }; -void w1_delay(unsigned long tm) +static void w1_delay(unsigned long tm) { udelay(tm * w1_delay_parm); } @@ -61,7 +61,7 @@ static u8 w1_read_bit(struct w1_master *dev); /** * Generates a write-0 or write-1 cycle and samples the level. */ -u8 w1_touch_bit(struct w1_master *dev, int bit) +static u8 w1_touch_bit(struct w1_master *dev, int bit) { if (dev->bus_master->touch_bit) return dev->bus_master->touch_bit(dev->bus_master->data, bit); @@ -108,6 +108,7 @@ void w1_write_8(struct w1_master *dev, u8 byte) for (i = 0; i < 8; ++i) w1_touch_bit(dev, (byte >> i) & 0x1); } +EXPORT_SYMBOL_GPL(w1_write_8); /** @@ -176,7 +177,7 @@ u8 w1_triplet(struct w1_master *dev, int bdir) * @param dev the master device * @return the byte read */ -u8 w1_read_8(struct w1_master * dev) +static u8 w1_read_8(struct w1_master * dev) { int i; u8 res = 0; @@ -208,6 +209,7 @@ void w1_write_block(struct w1_master *dev, const u8 *buf, int len) for (i = 0; i < len; ++i) w1_write_8(dev, buf[i]); } +EXPORT_SYMBOL_GPL(w1_write_block); /** * Reads a series of bytes. @@ -232,6 +234,7 @@ u8 w1_read_block(struct w1_master *dev, u8 *buf, int len) return ret; } +EXPORT_SYMBOL_GPL(w1_read_block); /** * Issues a reset bus sequence. @@ -257,6 +260,7 @@ int w1_reset_bus(struct w1_master *dev) return result; } +EXPORT_SYMBOL_GPL(w1_reset_bus); u8 w1_calc_crc8(u8 * data, int len) { @@ -267,14 +271,15 @@ u8 w1_calc_crc8(u8 * data, int len) return crc; } +EXPORT_SYMBOL_GPL(w1_calc_crc8); -void w1_search_devices(struct w1_master *dev, w1_slave_found_callback cb) +void w1_search_devices(struct w1_master *dev, u8 search_type, w1_slave_found_callback cb) { dev->attempts++; if (dev->bus_master->search) - dev->bus_master->search(dev->bus_master->data, cb); + dev->bus_master->search(dev->bus_master->data, search_type, cb); else - w1_search(dev, cb); + w1_search(dev, search_type, cb); } /** @@ -299,14 +304,4 @@ int w1_reset_select_slave(struct w1_slave *sl) } return 0; } - -EXPORT_SYMBOL(w1_touch_bit); -EXPORT_SYMBOL(w1_write_8); -EXPORT_SYMBOL(w1_read_8); -EXPORT_SYMBOL(w1_reset_bus); -EXPORT_SYMBOL(w1_calc_crc8); -EXPORT_SYMBOL(w1_delay); -EXPORT_SYMBOL(w1_read_block); -EXPORT_SYMBOL(w1_write_block); -EXPORT_SYMBOL(w1_search_devices); -EXPORT_SYMBOL(w1_reset_select_slave); +EXPORT_SYMBOL_GPL(w1_reset_select_slave); diff --git a/drivers/w1/w1_io.h b/drivers/w1/w1_io.h index 232860184a2..9a76d2ad69c 100644 --- a/drivers/w1/w1_io.h +++ b/drivers/w1/w1_io.h @@ -24,11 +24,8 @@ #include "w1.h" -void w1_delay(unsigned long); -u8 w1_touch_bit(struct w1_master *, int); u8 w1_triplet(struct w1_master *dev, int bdir); void w1_write_8(struct w1_master *, u8); -u8 w1_read_8(struct w1_master *); int w1_reset_bus(struct w1_master *); u8 w1_calc_crc8(u8 *, int); void w1_write_block(struct w1_master *, const u8 *, int); diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c index 328645da797..65c5ebd0787 100644 --- a/drivers/w1/w1_netlink.c +++ b/drivers/w1/w1_netlink.c @@ -21,72 +21,225 @@ #include <linux/skbuff.h> #include <linux/netlink.h> +#include <linux/connector.h> #include "w1.h" #include "w1_log.h" #include "w1_netlink.h" -#ifndef NETLINK_DISABLED +#if defined(CONFIG_W1_CON) && (defined(CONFIG_CONNECTOR) || (defined(CONFIG_CONNECTOR_MODULE) && defined(CONFIG_W1_MODULE))) void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg) { - unsigned int size; - struct sk_buff *skb; - struct w1_netlink_msg *data; - struct nlmsghdr *nlh; + char buf[sizeof(struct cn_msg) + sizeof(struct w1_netlink_msg)]; + struct cn_msg *m = (struct cn_msg *)buf; + struct w1_netlink_msg *w = (struct w1_netlink_msg *)(m+1); - if (!dev->nls) - return; + memset(buf, 0, sizeof(buf)); - size = NLMSG_SPACE(sizeof(struct w1_netlink_msg)); + m->id.idx = CN_W1_IDX; + m->id.val = CN_W1_VAL; - skb = alloc_skb(size, GFP_ATOMIC); - if (!skb) { - dev_err(&dev->dev, "skb_alloc() failed.\n"); - return; - } + m->seq = dev->seq++; + m->len = sizeof(struct w1_netlink_msg); + + memcpy(w, msg, sizeof(struct w1_netlink_msg)); + + cn_netlink_send(m, 0, GFP_KERNEL); +} + +static int w1_process_command_master(struct w1_master *dev, struct cn_msg *msg, + struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) +{ + dev_dbg(&dev->dev, "%s: %s: cmd=%02x, len=%u.\n", + __func__, dev->name, cmd->cmd, cmd->len); + + if (cmd->cmd != W1_CMD_SEARCH && cmd->cmd != W1_CMD_ALARM_SEARCH) + return -EINVAL; + + w1_search_process(dev, (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH); + return 0; +} + +static int w1_send_read_reply(struct w1_slave *sl, struct cn_msg *msg, + struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) +{ + void *data; + struct w1_netlink_msg *h; + struct w1_netlink_cmd *c; + struct cn_msg *cm; + int err; + + data = kzalloc(sizeof(struct cn_msg) + + sizeof(struct w1_netlink_msg) + + sizeof(struct w1_netlink_cmd) + + cmd->len, GFP_KERNEL); + if (!data) + return -ENOMEM; + + cm = (struct cn_msg *)(data); + h = (struct w1_netlink_msg *)(cm + 1); + c = (struct w1_netlink_cmd *)(h + 1); + + memcpy(cm, msg, sizeof(struct cn_msg)); + memcpy(h, hdr, sizeof(struct w1_netlink_msg)); + memcpy(c, cmd, sizeof(struct w1_netlink_cmd)); - nlh = NLMSG_PUT(skb, 0, dev->seq++, NLMSG_DONE, size - sizeof(*nlh)); + cm->ack = msg->seq+1; + cm->len = sizeof(struct w1_netlink_msg) + sizeof(struct w1_netlink_cmd) + cmd->len; - data = (struct w1_netlink_msg *)NLMSG_DATA(nlh); + h->len = sizeof(struct w1_netlink_cmd) + cmd->len; - memcpy(data, msg, sizeof(struct w1_netlink_msg)); + memcpy(c->data, cmd->data, c->len); - NETLINK_CB(skb).dst_group = dev->groups; - netlink_broadcast(dev->nls, skb, 0, dev->groups, GFP_ATOMIC); + err = cn_netlink_send(cm, 0, GFP_KERNEL); -nlmsg_failure: - return; + kfree(data); + + return err; } -int dev_init_netlink(struct w1_master *dev) +static int w1_process_command_slave(struct w1_slave *sl, struct cn_msg *msg, + struct w1_netlink_msg *hdr, struct w1_netlink_cmd *cmd) { - dev->nls = netlink_kernel_create(NETLINK_W1, 1, NULL, THIS_MODULE); - if (!dev->nls) { - printk(KERN_ERR "Failed to create new netlink socket(%u) for w1 master %s.\n", - NETLINK_W1, dev->dev.bus_id); + int err = 0; + + dev_dbg(&sl->master->dev, "%s: %02x.%012llx.%02x: cmd=%02x, len=%u.\n", + __func__, sl->reg_num.family, (unsigned long long)sl->reg_num.id, sl->reg_num.crc, + cmd->cmd, cmd->len); + + switch (cmd->cmd) { + case W1_CMD_READ: + w1_read_block(sl->master, cmd->data, cmd->len); + w1_send_read_reply(sl, msg, hdr, cmd); + break; + case W1_CMD_WRITE: + w1_write_block(sl->master, cmd->data, cmd->len); + break; + case W1_CMD_SEARCH: + case W1_CMD_ALARM_SEARCH: + w1_search_process(sl->master, + (cmd->cmd == W1_CMD_ALARM_SEARCH)?W1_ALARM_SEARCH:W1_SEARCH); + break; + default: + err = -1; + break; } - return 0; + return err; } -void dev_fini_netlink(struct w1_master *dev) +static void w1_cn_callback(void *data) { - if (dev->nls && dev->nls->sk_socket) - sock_release(dev->nls->sk_socket); + struct cn_msg *msg = data; + struct w1_netlink_msg *m = (struct w1_netlink_msg *)(msg + 1); + struct w1_netlink_cmd *cmd; + struct w1_slave *sl; + struct w1_master *dev; + int err = 0; + + while (msg->len && !err) { + struct w1_reg_num id; + u16 mlen = m->len; + u8 *cmd_data = m->data; + + dev = NULL; + sl = NULL; + + memcpy(&id, m->id.id, sizeof(id)); +#if 0 + printk("%s: %02x.%012llx.%02x: type=%02x, len=%u.\n", + __func__, id.family, (unsigned long long)id.id, id.crc, m->type, m->len); +#endif + if (m->len + sizeof(struct w1_netlink_msg) > msg->len) { + err = -E2BIG; + break; + } + + if (!mlen) + goto out_cont; + + if (m->type == W1_MASTER_CMD) { + dev = w1_search_master_id(m->id.mst.id); + } else if (m->type == W1_SLAVE_CMD) { + sl = w1_search_slave(&id); + if (sl) + dev = sl->master; + } + + if (!dev) { + err = -ENODEV; + goto out_cont; + } + + mutex_lock(&dev->mutex); + + if (sl && w1_reset_select_slave(sl)) { + err = -ENODEV; + goto out_up; + } + + while (mlen) { + cmd = (struct w1_netlink_cmd *)cmd_data; + + if (cmd->len + sizeof(struct w1_netlink_cmd) > mlen) { + err = -E2BIG; + break; + } + + if (sl) + w1_process_command_slave(sl, msg, m, cmd); + else + w1_process_command_master(dev, msg, m, cmd); + + cmd_data += cmd->len + sizeof(struct w1_netlink_cmd); + mlen -= cmd->len + sizeof(struct w1_netlink_cmd); + } +out_up: + atomic_dec(&dev->refcnt); + if (sl) + atomic_dec(&sl->refcnt); + mutex_unlock(&dev->mutex); +out_cont: + msg->len -= sizeof(struct w1_netlink_msg) + m->len; + m = (struct w1_netlink_msg *)(((u8 *)m) + sizeof(struct w1_netlink_msg) + m->len); + + /* + * Let's allow requests for nonexisting devices. + */ + if (err == -ENODEV) + err = 0; + } +#if 0 + if (err) { + printk("%s: malformed message. Dropping.\n", __func__); + } +#endif } -#else -#warning Netlink support is disabled. Please compile with NET support enabled. +int w1_init_netlink(void) +{ + struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL}; + + return cn_add_callback(&w1_id, "w1", &w1_cn_callback); +} + +void w1_fini_netlink(void) +{ + struct cb_id w1_id = {.idx = CN_W1_IDX, .val = CN_W1_VAL}; + + cn_del_callback(&w1_id); +} +#else void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg) { } -int dev_init_netlink(struct w1_master *dev) +int w1_init_netlink(void) { return 0; } -void dev_fini_netlink(struct w1_master *dev) +void w1_fini_netlink(void) { } #endif diff --git a/drivers/w1/w1_netlink.h b/drivers/w1/w1_netlink.h index eb0c8b3152c..56122b9e929 100644 --- a/drivers/w1/w1_netlink.h +++ b/drivers/w1/w1_netlink.h @@ -23,6 +23,7 @@ #define __W1_NETLINK_H #include <asm/types.h> +#include <linux/connector.h> #include "w1.h" @@ -31,29 +32,43 @@ enum w1_netlink_message_types { W1_SLAVE_REMOVE, W1_MASTER_ADD, W1_MASTER_REMOVE, + W1_MASTER_CMD, + W1_SLAVE_CMD, }; struct w1_netlink_msg { __u8 type; - __u8 reserved[3]; - union - { - struct w1_reg_num id; - __u64 w1_id; - struct - { + __u8 reserved; + __u16 len; + union { + __u8 id[8]; + struct w1_mst { __u32 id; - __u32 pid; + __u32 res; } mst; } id; + __u8 data[0]; +}; + +#define W1_CMD_READ 0x0 +#define W1_CMD_WRITE 0x1 +#define W1_CMD_SEARCH 0x2 +#define W1_CMD_ALARM_SEARCH 0x3 + +struct w1_netlink_cmd +{ + __u8 cmd; + __u8 res; + __u16 len; + __u8 data[0]; }; #ifdef __KERNEL__ void w1_netlink_send(struct w1_master *, struct w1_netlink_msg *); -int dev_init_netlink(struct w1_master *dev); -void dev_fini_netlink(struct w1_master *dev); +int w1_init_netlink(void); +void w1_fini_netlink(void); #endif /* __KERNEL__ */ #endif /* __W1_NETLINK_H */ |