aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/viodasd.c2
-rw-r--r--drivers/cdrom/viocd.c2
-rw-r--r--drivers/char/agp/Kconfig3
-rw-r--r--drivers/char/hvc_console.c4
-rw-r--r--drivers/char/hvc_rtas.c37
-rw-r--r--drivers/char/hvsi.c2
-rw-r--r--drivers/char/viotape.c2
-rw-r--r--drivers/cpufreq/cpufreq_conservative.c12
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c12
-rw-r--r--drivers/hwmon/Kconfig65
-rw-r--r--drivers/hwmon/Makefile4
-rw-r--r--drivers/hwmon/abituguru.c1415
-rw-r--r--drivers/hwmon/f71805f.c15
-rw-r--r--drivers/hwmon/hdaps.c8
-rw-r--r--drivers/hwmon/hwmon-vid.c44
-rw-r--r--drivers/hwmon/lm70.c165
-rw-r--r--drivers/hwmon/lm83.c50
-rw-r--r--drivers/hwmon/smsc47m192.c648
-rw-r--r--drivers/hwmon/w83627ehf.c170
-rw-r--r--drivers/hwmon/w83791d.c1255
-rw-r--r--drivers/hwmon/w83792d.c86
-rw-r--r--drivers/i2c/busses/Kconfig26
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-i801.c154
-rw-r--r--drivers/i2c/busses/i2c-nforce2.c38
-rw-r--r--drivers/i2c/busses/i2c-ocores.c341
-rw-r--r--drivers/i2c/busses/i2c-piix4.c33
-rw-r--r--drivers/i2c/busses/scx200_acb.c202
-rw-r--r--drivers/i2c/chips/Kconfig8
-rw-r--r--drivers/i2c/chips/m41t00.c346
-rw-r--r--drivers/i2c/i2c-core.c4
-rw-r--r--drivers/i2c/i2c-dev.c5
-rw-r--r--drivers/macintosh/via-pmu.c4
-rw-r--r--drivers/net/8139cp.c179
-rw-r--r--drivers/net/8390.c10
-rw-r--r--drivers/net/Kconfig23
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/arm/at91_ether.c156
-rw-r--r--drivers/net/arm/at91_ether.h1
-rw-r--r--drivers/net/b44.c277
-rw-r--r--drivers/net/b44.h7
-rw-r--r--drivers/net/forcedeth.c2
-rw-r--r--drivers/net/ibmveth.c2
-rw-r--r--drivers/net/ioc3-eth.c2
-rw-r--r--drivers/net/iseries_veth.c27
-rw-r--r--drivers/net/myri10ge/myri10ge.c10
-rw-r--r--drivers/net/netx-eth.c516
-rw-r--r--drivers/net/pcmcia/xirc2ps_cs.c2
-rw-r--r--drivers/net/phy/Kconfig5
-rw-r--r--drivers/net/phy/Makefile1
-rw-r--r--drivers/net/phy/vitesse.c112
-rw-r--r--drivers/net/s2io.c48
-rw-r--r--drivers/net/sundance.c8
-rw-r--r--drivers/net/tokenring/olympic.c4
-rw-r--r--drivers/net/via-velocity.c6
-rw-r--r--drivers/net/wan/c101.c6
-rw-r--r--drivers/net/wan/hdlc_generic.c24
-rw-r--r--drivers/net/wan/n2.c5
-rw-r--r--drivers/net/wan/pci200syn.c1
-rw-r--r--drivers/net/wan/wanxl.c12
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx.h100
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c33
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_leds.c4
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_main.c221
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_phy.c9
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_pio.c44
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_pio.h13
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c38
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_wx.c107
-rw-r--r--drivers/net/wireless/ipw2200.c41
-rw-r--r--drivers/net/wireless/ipw2200.h1
-rw-r--r--drivers/net/wireless/wavelan.c18
-rw-r--r--drivers/pci/Makefile6
-rw-r--r--drivers/pci/bus.c21
-rw-r--r--drivers/pci/msi-altix.c210
-rw-r--r--drivers/pci/msi-apic.c100
-rw-r--r--drivers/pci/msi.c285
-rw-r--r--drivers/pci/msi.h133
-rw-r--r--drivers/pci/pci-acpi.c16
-rw-r--r--drivers/pci/pci-sysfs.c45
-rw-r--r--drivers/pci/pci.c16
-rw-r--r--drivers/pci/pci.h2
-rw-r--r--drivers/pci/probe.c45
-rw-r--r--drivers/pci/quirks.c47
-rw-r--r--drivers/pci/remove.c12
-rw-r--r--drivers/pci/search.c32
-rw-r--r--drivers/pci/setup-bus.c5
-rw-r--r--drivers/pci/setup-res.c40
-rw-r--r--drivers/scsi/qla1280.c24
-rw-r--r--drivers/scsi/sata_vsc.c11
-rw-r--r--drivers/serial/Kconfig19
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/amba-pl010.c2
-rw-r--r--drivers/serial/netx-serial.c749
-rw-r--r--drivers/video/console/mdacon.c2
-rw-r--r--drivers/video/console/vgacon.c19
-rw-r--r--drivers/video/vga16fb.c2
-rw-r--r--drivers/w1/Kconfig14
-rw-r--r--drivers/w1/Makefile4
-rw-r--r--drivers/w1/masters/Kconfig27
-rw-r--r--drivers/w1/masters/Makefile7
-rw-r--r--drivers/w1/masters/ds2482.c24
-rw-r--r--drivers/w1/masters/ds2490.c (renamed from drivers/w1/masters/dscore.c)434
-rw-r--r--drivers/w1/masters/ds_w1_bridge.c174
-rw-r--r--drivers/w1/masters/dscore.h166
-rw-r--r--drivers/w1/slaves/Kconfig2
-rw-r--r--drivers/w1/slaves/w1_ds2433.c21
-rw-r--r--drivers/w1/slaves/w1_smem.c1
-rw-r--r--drivers/w1/slaves/w1_therm.c13
-rw-r--r--drivers/w1/w1.c263
-rw-r--r--drivers/w1/w1.h41
-rw-r--r--drivers/w1/w1_family.c18
-rw-r--r--drivers/w1/w1_family.h3
-rw-r--r--drivers/w1/w1_int.c16
-rw-r--r--drivers/w1/w1_io.c31
-rw-r--r--drivers/w1/w1_io.h3
-rw-r--r--drivers/w1/w1_netlink.c219
-rw-r--r--drivers/w1/w1_netlink.h35
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 */