[PATCHv0] hwmon: Add support for GMT G751 Temp. Sensor and Thermal Watchdog

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds support for GMT G751 Temperature Sensor and Thermal
Watchdog I2C chip. It has been tested via DT on a Netgear ReadyNAS
2120 (Marvell Armada XP based ARM device).

Signed-off-by: Arnaud Ebalard <arno@xxxxxxxxxxxx>
---
 Documentation/devicetree/bindings/hwmon/g751.txt   |  24 +
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 Documentation/hwmon/g751                           |  44 ++
 drivers/hwmon/Kconfig                              |  11 +
 drivers/hwmon/Makefile                             |   1 +
 drivers/hwmon/g751.c                               | 481 +++++++++++++++++++++
 include/linux/platform_data/g751.h                 |  35 ++
 7 files changed, 597 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/g751.txt
 create mode 100644 Documentation/hwmon/g751
 create mode 100644 drivers/hwmon/g751.c
 create mode 100644 include/linux/platform_data/g751.h

diff --git a/Documentation/devicetree/bindings/hwmon/g751.txt b/Documentation/devicetree/bindings/hwmon/g751.txt
new file mode 100644
index 0000000..ebec788
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/g751.txt
@@ -0,0 +1,24 @@
+GMT G751 Digital Temperature Sensor and Thermal Watchdog
+
+Required node properties:
+
+ - "compatible": must be either "gmt,g751"
+ - "reg": I2C bus address of the device
+
+Optional properties:
+
+ - "polarity": Over temperature Shutdown (OS) output polarity. Accepted values
+	       are 0 and 1. 0 (the default) is used to make the output active
+	       low. 1 makes the output active high.
+
+Additional information on operational parameters for the device is available
+in Documentation/hwmon/g751. A detailed datasheet for the device is available
+at http://natisbad.org/NAS4/refs/GMT_G751.pdf.
+
+Example g751 node:
+
+   g751: g751@4c {
+	compatible = "gmt,g751";
+	reg = <0x4c>;
+	polarity = <1>; /* OS output Active High */
+   };
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 2956800..634c35f 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -28,6 +28,7 @@ est	ESTeem Wireless Modems
 fsl	Freescale Semiconductor
 GEFanuc	GE Fanuc Intelligent Platforms Embedded Systems, Inc.
 gef	GE Fanuc Intelligent Platforms Embedded Systems, Inc.
+gmt	Global Mixed-mode Technology Inc.
 hisilicon	Hisilicon Limited.
 hp	Hewlett Packard
 ibm	International Business Machines (IBM)
diff --git a/Documentation/hwmon/g751 b/Documentation/hwmon/g751
new file mode 100644
index 0000000..3508d53
--- /dev/null
+++ b/Documentation/hwmon/g751
@@ -0,0 +1,44 @@
+Kernel driver g751
+==================
+
+The GMT G751 is an I2C digital temperature sensor and thermal watchdog. For
+additional information, a detailed datasheet of the chip is available at
+http://natisbad.org/NAS4/refs/GMT_G751.pdf.
+
+sysfs bindings (found in a subdirectory of/sys/bus/i2c/drivers/g751/) are
+described below. They are available to the user to control the operation
+of the device. The main information provided by the device (temperature,
+via temp_input) is usually used by a userland daemon like fancontrol.
+
+Note that polarity of Over temperature Shutdown (OS) output is considered
+a hardware characteristics of the system and can be modified via devicetree
+bindings documented in Documentation/devicetree/bindings/hwmon/g751.txt or
+using a specific platform_data structure in board initialization file (see
+include/linux/platform_data/g751.h).
+
+temp_input: current temperature input value in millidegree Celsius. This
+      parameter is RO.
+
+thyst: defined Thyst value in millidegree Celsius. See 'mode' below for
+      details. Default value depends on G751 flavour (45000 for G751-1,
+      75000 for G751-2). This parameter is RW.
+
+tos: defined Tos value in millidegree Celsius. See 'mode' below for details.
+      Default value depends on G751 flavour (50000 for G751-1, 80000 for
+      G751-2). This parameter is RW.
+
+fault_queue: number of faults necessary to detect before setting OS output.
+      This can be used to avoid false tripping due to noise. Valid values
+      are 1 (default), 2, 4 and 6. This parameter is RW.
+
+mode: used to toggle between comparatore mode (0, default mode) and interrupt
+      mode (1). In comparator mode, the OS output behaves like a thermostat
+      and becomes active when temperature exceeds Tos limit. It then leaves
+      the active state when the temperature drops again below Thyst. In
+      interrupt mode, exceeding Tos also makes OS output active in which case
+      it will remain active until reading any register. It can then be activated
+      again only after temperature goes below Thyst. This parameter is RW.
+
+shutdown: when set to 1 (0 being the default), the G751 goes to low power
+      shutdown mode. Placing G751 in shutdown mode also has the effect of
+      resetting the OS output. This parameter is RW.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index b3ab9d4..524d1d7 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -451,6 +451,17 @@ config SENSORS_FSCHMD
 	  This driver can also be built as a module.  If so, the module
 	  will be called fschmd.
 
+config SENSORS_G751
+	tristate "GMT G751"
+	depends on I2C
+	help
+	  If you say yes here you get support for Global Mixed-mode
+	  Technology Inc G751 Digital Temperature Sensor and Thermal
+	  Watchdog.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called g751.
+
 config SENSORS_G760A
 	tristate "GMT G760A"
 	depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index ec7cde0..5c8bfc6 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
 obj-$(CONFIG_SENSORS_F75375S)	+= f75375s.o
 obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o
 obj-$(CONFIG_SENSORS_FSCHMD)	+= fschmd.o
+obj-$(CONFIG_SENSORS_G751)	+= g751.o
 obj-$(CONFIG_SENSORS_G760A)	+= g760a.o
 obj-$(CONFIG_SENSORS_G762)	+= g762.o
 obj-$(CONFIG_SENSORS_GL518SM)	+= gl518sm.o
diff --git a/drivers/hwmon/g751.c b/drivers/hwmon/g751.c
new file mode 100644
index 0000000..9c85c41
--- /dev/null
+++ b/drivers/hwmon/g751.c
@@ -0,0 +1,481 @@
+/*
+ * g751 - Driver for the Global Mixed-mode Technology Inc. G751 digital
+ *        temperature sensor and thermal watchdog chip.
+ *
+ * Copyright (C) 2013, Arnaud EBALARD <arno@xxxxxxxxxxxx>
+ *
+ * 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.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_data/g751.h>
+
+#define DRVNAME "g751"
+
+static const struct i2c_device_id g751_id[] = {
+	{ "g751", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, g751_id);
+
+/* Four data registers can be addressed via a pointer register */
+enum g751_data_reg {
+	G751_REG_TEMP  = 0x00, /* 16-bit reg */
+	G751_REG_CONF  = 0x01, /*  8-bit reg */
+	G751_REG_THYST = 0x02, /* 16-bit reg */
+	G751_REG_TOS   = 0x03, /* 16-bit reg */
+};
+
+/* Provide size of given register */
+#define G751_DATA_REG_LEN(reg) (((reg) == G751_REG_CONF) ? 1 : 2)
+
+/* Configuration register bits: shifts and masks */
+#define G751_CONF_FQUEUE_SHIFT   3
+#define G751_CONF_FQUEUE_MASK    0x03
+#define G751_CONF_POLARITY_SHIFT 2
+#define G751_CONF_POLARITY_MASK  0x02
+#define G751_CONF_MODE_SHIFT     1
+#define G751_CONF_MODE_MASK      0x01
+#define G751_CONF_SHUTDOWN_SHIFT 0
+#define G751_CONF_SHUTDOWN_MASK  0x01
+
+/* Temperature conversion helpers from/to register value */
+static inline void temp_from_reg(int32_t *temp, u8 *regval)
+{
+	int8_t *buf = (int8_t *)(regval);
+	*temp = (buf[0]*1000 + ((buf[1] & 0x80) ? 500 : 0));
+}
+
+static inline void temp_to_reg(u8 *regval, int32_t temp)
+{
+	regval[0] = (temp < 0 && temp/1000 == 0) ? 0xff : temp/1000;
+	regval[1] = ((temp/500) & 0x1) ? 0x80 : 0x00;
+}
+
+struct g751_data {
+	struct i2c_client *client;
+	struct device *hwmon_dev;
+	struct mutex lock;
+};
+
+/*
+ * Read content of 'reg' register and put result in 'val' buffer if
+ * everything went ok; 0 is returned in that case. A negative value
+ * is returned on error, in which case 'val' is not updated.
+ */
+static int g751_reg_read(struct device *dev, u8 reg, u8 *val)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	u8 buf[2] = { reg , 0 };
+	struct i2c_msg msgs[2] = {
+		{
+			.addr = client->addr,
+			.flags = client->flags,
+			.len = sizeof(u8),
+			.buf = buf
+		},
+		{
+			.addr = client->addr,
+			.flags = client->flags | I2C_M_RD,
+			.len = G751_DATA_REG_LEN(reg),
+			.buf = buf
+		}
+	};
+	int ret;
+
+	BUG_ON(reg > 3);
+
+	ret = i2c_transfer(client->adapter, msgs, 2);
+	if (ret < 0) {
+		dev_err(&client->dev, "%s: register read failed\n", __func__);
+		return ret;
+	}
+	memcpy(val, buf, G751_DATA_REG_LEN(reg));
+
+	return 0;
+}
+
+/*
+ * Write content of given 'val' buffer to 'reg' register. 0 is returned
+ * on success. A negative value is returned on error.
+ */
+static int g751_reg_write(struct device *dev, u8 reg, u8 *val)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	u8 buf[3] = { reg, 0, 0 };
+	struct i2c_msg msgs[1] = {
+		{
+			.addr = client->addr,
+			.flags = client->flags,
+			.len = G751_DATA_REG_LEN(reg) + 1,
+			.buf = buf
+		}
+	};
+	int ret;
+
+	BUG_ON(reg > 3 || reg == 0); /* temp reg (0) is read-only */
+
+	memcpy(&buf[1], val, G751_DATA_REG_LEN(reg));
+	ret = i2c_transfer(client->adapter, msgs, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "%s: register write failed\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+/* Generic helper to extract some specific bits of conf register */
+static int conf_reg_bits_get(struct device *dev, u8 *val,
+			     u8 bitshift, u8 bitmask)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct g751_data *data = i2c_get_clientdata(client);
+	u8 regval;
+	int ret;
+
+	mutex_lock(&data->lock);
+	ret = g751_reg_read(dev, G751_REG_CONF, &regval);
+	mutex_unlock(&data->lock);
+	if (ret < 0)
+		return ret;
+
+	*val = (regval >> bitshift) & bitmask;
+
+	return 0;
+}
+
+/* Generic helper to set some specific bits of conf register */
+static int conf_reg_bits_set(struct device *dev, u8 val,
+			     u8 bitshift, u8 bitmask)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct g751_data *data = i2c_get_clientdata(client);
+	u8 regval;
+	int ret;
+
+	mutex_lock(&data->lock);
+	ret = g751_reg_read(dev, G751_REG_CONF, &regval);
+	if (ret < 0)
+		goto out;
+
+	regval &= ~(bitmask << bitshift);
+	regval |= val << bitshift;
+
+	ret = g751_reg_write(dev, G751_REG_CONF, &regval);
+ out:
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+/*
+ * sysfs
+ */
+
+#define show_temp(value, reg)						\
+static ssize_t show_##value(struct device *dev, struct device_attribute *da, \
+			    char *buf) \
+{ \
+	u8 regval[2];					\
+	int32_t temp;					\
+	int ret;					\
+							\
+	ret = g751_reg_read(dev, reg, regval);		\
+	if (ret < 0)					\
+		return ret;				\
+	temp_from_reg(&temp, regval);			\
+							\
+	return sprintf(buf, "%d\n", temp);		\
+}
+
+show_temp(temp_input, G751_REG_TEMP);
+show_temp(temp_hyst, G751_REG_THYST);
+show_temp(temp_os, G751_REG_TOS);
+
+#define set_temp(value, reg)						\
+static ssize_t set_##value(struct device *dev, struct device_attribute *da, \
+			   const char *buf, size_t count) \
+{ \
+	u8 regval[2];					\
+	int32_t temp;					\
+	int ret;					\
+							\
+	ret = kstrtos32(buf, 10, &temp);		\
+	if (ret)					\
+		return ret;				\
+	temp_to_reg(regval, temp);			\
+							\
+	ret = g751_reg_write(dev, reg, regval);		\
+	if (ret < 0)					\
+		return ret;				\
+							\
+	return count;					\
+}
+
+set_temp(temp_hyst, G751_REG_THYST);
+set_temp(temp_os, G751_REG_TOS);
+
+/*
+ * Read and write functions for fault_queue sysfs file. Get and set
+ * fault_queue length (either 1 (default), 2, 4 or 6).
+ */
+static ssize_t show_fqueue(struct device *dev, struct device_attribute *da,
+			   char *buf)
+{
+	u8 regval;
+	int ret;
+
+	ret = conf_reg_bits_get(dev, &regval, G751_CONF_FQUEUE_SHIFT,
+				G751_CONF_FQUEUE_MASK);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", regval ? regval * 2 : 1);
+}
+
+static ssize_t set_fqueue(struct device *dev,
+			  struct device_attribute *da,
+			  const char *buf, size_t count)
+{
+	int ret;
+	u8 val;
+
+	if (kstrtou8(buf, 10, &val))
+		return -EINVAL;
+
+	switch (val) {
+	case 1:
+		val = 0;
+		break;
+	case 2:
+	case 4:
+	case 6:
+		val /= 2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = conf_reg_bits_set(dev, val, G751_CONF_FQUEUE_SHIFT,
+				G751_CONF_FQUEUE_MASK);
+
+	return ret < 0 ? ret : count;
+}
+
+/*
+ * Read and write functions for mode sysfs file. Get and set mode (0 for
+ * comparator mode and 1 for interrupt mode).
+ */
+static ssize_t show_mode(struct device *dev, struct device_attribute *da,
+			 char *buf)
+{
+	u8 regval;
+	int ret;
+
+	ret = conf_reg_bits_get(dev, &regval, G751_CONF_MODE_SHIFT,
+				G751_CONF_MODE_MASK);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", regval);
+}
+
+static ssize_t set_mode(struct device *dev,
+			struct device_attribute *da,
+			const char *buf, size_t count)
+{
+	int ret;
+	u8 val;
+
+	if (kstrtou8(buf, 10, &val) || val > 1)
+		return -EINVAL;
+
+	ret = conf_reg_bits_set(dev, val, G751_CONF_MODE_SHIFT,
+				G751_CONF_MODE_MASK);
+
+	return ret < 0 ? ret : count;
+}
+
+/*
+ * Read and write functions for shutdown sysfs file. Get and set low power
+ * shutdown mode (1 for low power shutdown mode).
+ */
+static ssize_t show_shutdown(struct device *dev,
+			     struct device_attribute *da, char *buf)
+{
+	u8 regval;
+	int ret;
+
+	ret = conf_reg_bits_get(dev, &regval, G751_CONF_MODE_SHIFT,
+				G751_CONF_MODE_MASK);
+	if (ret < 0)
+		return ret;
+
+	return sprintf(buf, "%d\n", regval);
+}
+
+static ssize_t set_shutdown(struct device *dev,
+			    struct device_attribute *da,
+			    const char *buf, size_t count)
+{
+	int ret;
+	u8 val;
+
+	if (kstrtou8(buf, 10, &val) || val > 1)
+		return -EINVAL;
+
+	ret = conf_reg_bits_set(dev, val, G751_CONF_SHUTDOWN_SHIFT,
+				G751_CONF_SHUTDOWN_MASK);
+
+	return ret < 0 ? ret : count;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id g751_dt_match[] = {
+	{ .compatible = "gmt,g751" },
+	{ },
+};
+
+static void g751_of_import_polarity(struct i2c_client *client, int *pol)
+{
+	const __be32 *prop;
+	int len;
+
+	prop = of_get_property(client->dev.of_node, "polarity", &len);
+	if (!prop || len != sizeof(u32))
+		return;
+
+	*pol = !!be32_to_cpu(prop[0]);
+}
+#else
+static void g751_of_import_polarity(struct i2c_client *client, int *pol) {}
+#endif
+
+static DEVICE_ATTR(temp_input, S_IRUGO, show_temp_input, NULL);
+static DEVICE_ATTR(thyst, S_IRUGO|S_IWUSR, show_temp_hyst, set_temp_hyst);
+static DEVICE_ATTR(tos, S_IRUGO|S_IWUSR, show_temp_os, set_temp_os);
+static DEVICE_ATTR(fault_queue, S_IRUGO|S_IWUSR, show_fqueue, set_fqueue);
+static DEVICE_ATTR(mode, S_IRUGO|S_IWUSR, show_mode, set_mode);
+static DEVICE_ATTR(shutdown, S_IRUGO|S_IWUSR, show_shutdown, set_shutdown);
+
+/* Driver data */
+static struct attribute *g751_attributes[] = {
+	&dev_attr_temp_input.attr,
+	&dev_attr_thyst.attr,
+	&dev_attr_tos.attr,
+	&dev_attr_fault_queue.attr,
+	&dev_attr_mode.attr,
+	&dev_attr_shutdown.attr,
+	NULL
+};
+
+static const struct attribute_group g751_group = {
+	.name = "g751",
+	.attrs = g751_attributes,
+};
+
+static int g751_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct g751_platform_data *pdata;
+	struct g751_data *data;
+	int polarity = -1;
+	int ret;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return -ENODEV;
+
+	data = devm_kzalloc(&client->dev, sizeof(struct g751_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, data);
+	data->client = client;
+	mutex_init(&data->lock);
+
+	/* Change polarity if requested either via platform data or OF */
+	pdata = dev_get_platdata(&client->dev);
+	if (pdata)
+		polarity = !!pdata->polarity;
+	else
+		g751_of_import_polarity(client, &polarity);
+
+	if (polarity != -1) {
+		dev_dbg(&client->dev, "found polarity (%d)\n", polarity);
+
+		ret = conf_reg_bits_set(&client->dev, polarity,
+					G751_CONF_POLARITY_SHIFT,
+					G751_CONF_POLARITY_MASK);
+		if (ret)
+			dev_err(&client->dev, "unable to set polarity (%d)\n",
+				polarity);
+	}
+
+	/* Register sysfs hooks */
+	ret = sysfs_create_group(&client->dev.kobj, &g751_group);
+	if (ret)
+		goto out;
+
+	data->hwmon_dev = hwmon_device_register(&client->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		ret = PTR_ERR(data->hwmon_dev);
+		goto sysfs_clean;
+	}
+
+	return 0;
+
+ sysfs_clean:
+	sysfs_remove_group(&client->dev.kobj, &g751_group);
+
+ out:
+	return ret;
+}
+
+static int g751_remove(struct i2c_client *client)
+{
+	struct g751_data *data = i2c_get_clientdata(client);
+
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&client->dev.kobj, &g751_group);
+
+	return 0;
+}
+
+static struct i2c_driver g751_driver = {
+	.driver = {
+		.name = DRVNAME,
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(g751_dt_match),
+	},
+	.probe	  = g751_probe,
+	.remove	  = g751_remove,
+	.id_table = g751_id,
+};
+
+module_i2c_driver(g751_driver);
+
+MODULE_AUTHOR("Arnaud EBALARD <arno@xxxxxxxxxxxx>");
+MODULE_DESCRIPTION("GMT G751 driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/platform_data/g751.h b/include/linux/platform_data/g751.h
new file mode 100644
index 0000000..fec0415
--- /dev/null
+++ b/include/linux/platform_data/g751.h
@@ -0,0 +1,35 @@
+/*
+ * Platform data structure for g751 temperature sensor and thermal
+ * watchdog driver
+ *
+ * Copyright (C) 2013, Arnaud EBALARD <arno@xxxxxxxxxxxx>
+ *
+ * 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.
+ */
+#ifndef __LINUX_PLATFORM_DATA_G751_H__
+#define __LINUX_PLATFORM_DATA_G751_H__
+
+/*
+ * Following structure can be used to set g751 driver platform
+ * specific data during board init, i.e. over temperature (OS)
+ * output polarity: 0 for active low (the default), 1 for active
+ * high).
+ */
+
+struct g751_platform_data {
+	u8 polarity;
+};
+
+#endif /* __LINUX_PLATFORM_DATA_G751_H__ */
-- 
1.8.4.rc3


_______________________________________________
lm-sensors mailing list
lm-sensors@xxxxxxxxxxxxxx
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors




[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux