mirror of
https://github.com/Telecominfraproject/OpenNetworkLinux.git
synced 2025-12-25 17:27:01 +00:00
Merge pull request #94 from sonoble/update_316
Updates 3.16 kernel to include Sonic patches, fixes Makefile
This commit is contained in:
@@ -16,4 +16,4 @@ kernel:
|
||||
$(MAKE) -C $(ONL)/packages/base/any/kernels/3.16+deb8/configs/x86_64-all K_TARGET_DIR=$(THIS_DIR) $(ONL_MAKE_PARALLEL)
|
||||
|
||||
clean:
|
||||
rm -rf linux-3.16-*
|
||||
rm -rf linux-3.16*
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
18
packages/base/any/kernels/3.16+deb8/patches/changelog.patch
Normal file
18
packages/base/any/kernels/3.16+deb8/patches/changelog.patch
Normal file
@@ -0,0 +1,18 @@
|
||||
--- debian/changelog 2015-08-04 00:50:04.000000000 +0000
|
||||
+++ changelog 2015-12-20 04:20:25.032779900 +0000
|
||||
@@ -1,3 +1,15 @@
|
||||
+linux (3.16.7-ckt11-2+acs8u2) acs; urgency=high
|
||||
+
|
||||
+ * add driver patches for MLNX SN2700
|
||||
+
|
||||
+ -- Guohan Lu <gulv@microsoft.com> Sun, 19 Dec 2015 01:50:04 +0100
|
||||
+
|
||||
+linux (3.16.7-ckt11-2+acs8u1) acs; urgency=high
|
||||
+
|
||||
+ * add support for S6000
|
||||
+
|
||||
+ -- Shuotian Cheng <shuche@microsoft.com> Sun, 19 Dec 2015 01:50:04 +0100
|
||||
+
|
||||
linux (3.16.7-ckt11-1+deb8u3) jessie-security; urgency=high
|
||||
|
||||
* path_openat(): fix double fput() (CVE-2015-5706)
|
||||
@@ -0,0 +1,146 @@
|
||||
From f75a16bc0dfc83cf3df1db7ede4d7357e7be5952 Mon Sep 17 00:00:00 2001
|
||||
From: Chulei Wu <chulwu@microsoft.com>
|
||||
Date: Wed, 2 Mar 2016 04:09:53 +0000
|
||||
Subject: [PATCH] arista piix4 mux patch
|
||||
|
||||
---
|
||||
drivers/i2c/busses/i2c-piix4.c | 63 +++++++++++++++++++++++++++++++++++++-----
|
||||
1 file changed, 56 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
|
||||
index a6f54ba..eafc035 100644
|
||||
--- a/drivers/i2c/busses/i2c-piix4.c
|
||||
+++ b/drivers/i2c/busses/i2c-piix4.c
|
||||
@@ -128,6 +128,7 @@ static const struct dmi_system_id piix4_dmi_ibm[] = {
|
||||
|
||||
struct i2c_piix4_adapdata {
|
||||
unsigned short smba;
|
||||
+ int mux;
|
||||
};
|
||||
|
||||
static int piix4_setup(struct pci_dev *PIIX4_dev,
|
||||
@@ -528,6 +529,43 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static s32 piix4_access_mux(struct i2c_adapter * adap, u16 addr,
|
||||
+ unsigned short flags, char read_write,
|
||||
+ u8 command, int size, union i2c_smbus_data * data)
|
||||
+{
|
||||
+ static DEFINE_MUTEX(mux_mutex);
|
||||
+ struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap);
|
||||
+ int piix4_mux = adapdata->mux;
|
||||
+ static int last_mux = -1;
|
||||
+ s32 ret;
|
||||
+ unsigned short smba_idx = 0xcd6;
|
||||
+ u8 smb_en = 0x2c;
|
||||
+ u8 val;
|
||||
+
|
||||
+ if ( piix4_mux == -1 ) {
|
||||
+ return piix4_access(adap, addr, flags, read_write, command, size, data);
|
||||
+ }
|
||||
+
|
||||
+ mutex_lock(&mux_mutex);
|
||||
+
|
||||
+ if ( last_mux != piix4_mux ) {
|
||||
+ /* Select the correct bus mux*/
|
||||
+ outb_p(smb_en, smba_idx);
|
||||
+ val = inb_p(smba_idx + 1);
|
||||
+ val = (val & 0xf9) | (piix4_mux << 1);
|
||||
+ outb_p(val, smba_idx + 1);
|
||||
+
|
||||
+ last_mux = piix4_mux;
|
||||
+ dev_dbg(&adap->dev, "set mux to 0x%02x\n", piix4_mux);
|
||||
+ }
|
||||
+
|
||||
+ ret = piix4_access(adap, addr, flags, read_write, command, size, data);
|
||||
+
|
||||
+ mutex_unlock(&mux_mutex);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static u32 piix4_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
|
||||
@@ -536,7 +574,7 @@ static u32 piix4_func(struct i2c_adapter *adapter)
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm smbus_algorithm = {
|
||||
- .smbus_xfer = piix4_access,
|
||||
+ .smbus_xfer = piix4_access_mux,
|
||||
.functionality = piix4_func,
|
||||
};
|
||||
|
||||
@@ -569,7 +607,7 @@ static struct i2c_adapter *piix4_main_adapter;
|
||||
static struct i2c_adapter *piix4_aux_adapter;
|
||||
|
||||
static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
|
||||
- struct i2c_adapter **padap)
|
||||
+ struct i2c_adapter **padap, int mux)
|
||||
{
|
||||
struct i2c_adapter *adap;
|
||||
struct i2c_piix4_adapdata *adapdata;
|
||||
@@ -593,6 +631,7 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
|
||||
}
|
||||
|
||||
adapdata->smba = smba;
|
||||
+ adapdata->mux = mux;
|
||||
|
||||
/* set up the sysfs linkage to our parent device */
|
||||
adap->dev.parent = &dev->dev;
|
||||
@@ -618,6 +657,8 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
|
||||
static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
int retval;
|
||||
+ int mux = -1;
|
||||
+ int aux_smba;
|
||||
|
||||
if ((dev->vendor == PCI_VENDOR_ID_ATI &&
|
||||
dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
|
||||
@@ -633,7 +674,14 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
return retval;
|
||||
|
||||
/* Try to register main SMBus adapter, give up if we can't */
|
||||
- retval = piix4_add_adapter(dev, retval, &piix4_main_adapter);
|
||||
+ aux_smba = retval;
|
||||
+ if (dev->vendor == PCI_VENDOR_ID_AMD &&
|
||||
+ dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) {
|
||||
+ mux = -1;
|
||||
+ } else {
|
||||
+ mux = 0;
|
||||
+ }
|
||||
+ retval = piix4_add_adapter(dev, retval, &piix4_main_adapter, mux);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
|
||||
@@ -644,21 +692,22 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS) {
|
||||
if (dev->revision < 0x40) {
|
||||
retval = piix4_setup_aux(dev, id, 0x58);
|
||||
+ mux = -1;
|
||||
} else {
|
||||
- /* SB800 added aux bus too */
|
||||
- retval = piix4_setup_sb800(dev, id, 1);
|
||||
+ retval = aux_smba;
|
||||
+ mux = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->vendor == PCI_VENDOR_ID_AMD &&
|
||||
dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) {
|
||||
retval = piix4_setup_sb800(dev, id, 1);
|
||||
+ mux = -1;
|
||||
}
|
||||
-
|
||||
if (retval > 0) {
|
||||
/* Try to add the aux adapter if it exists,
|
||||
* piix4_add_adapter will clean up if this fails */
|
||||
- piix4_add_adapter(dev, retval, &piix4_aux_adapter);
|
||||
+ piix4_add_adapter(dev, retval, &piix4_aux_adapter, mux);
|
||||
}
|
||||
|
||||
return 0;
|
||||
--
|
||||
2.1.4
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
driver at24 fix odd length two byte access
|
||||
|
||||
From: Cumulus Networks <support@cumulusnetworks.com>
|
||||
|
||||
For I2C_SMBUS_WORD_DATA read accesses check if the access length is
|
||||
one or two bytes. For transactions that have an odd length eventualy
|
||||
we read 1 byte at the end to complete the request.
|
||||
|
||||
The previous code always used a count of 2, which works fine if the
|
||||
requested total length is even. If the requested length was odd,
|
||||
however, the code would cause a kernel OOPS.
|
||||
|
||||
The while (count) loop would go forever as count went from 1 to -1,
|
||||
never becoming zero. Also the return buffer would overrun.
|
||||
|
||||
This patch allows for reading an odd number of bytes in
|
||||
I2C_SMBUS_WORD_DATA mode.
|
||||
---
|
||||
drivers/misc/eeprom/at24.c | 6 ++++--
|
||||
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
|
||||
index d87f77f..9e9256a 100644
|
||||
--- a/drivers/misc/eeprom/at24.c
|
||||
+++ b/drivers/misc/eeprom/at24.c
|
||||
@@ -192,7 +192,8 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
|
||||
count = I2C_SMBUS_BLOCK_MAX;
|
||||
break;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
- count = 2;
|
||||
+ /* Check for odd length transaction */
|
||||
+ count = (count == 1) ? 1 : 2;
|
||||
break;
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
count = 1;
|
||||
@@ -237,7 +238,8 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
|
||||
status = i2c_smbus_read_word_data(client, offset);
|
||||
if (status >= 0) {
|
||||
buf[0] = status & 0xff;
|
||||
- buf[1] = status >> 8;
|
||||
+ if (count == 2)
|
||||
+ buf[1] = status >> 8;
|
||||
status = count;
|
||||
}
|
||||
break;
|
||||
@@ -0,0 +1,196 @@
|
||||
MAX6620 fix rpm calculation accuracy
|
||||
|
||||
From: Cumulus Networks <support@cumulusnetworks.com>
|
||||
|
||||
The driver only fills the most significant 8 bits of the fan tach
|
||||
count (11 bit value). Fixing the driver to use all of 11 bits for
|
||||
more accuracy.
|
||||
---
|
||||
drivers/hwmon/max6620.c | 105 +++++++++++++++++++++--------------------------
|
||||
1 file changed, 46 insertions(+), 59 deletions(-)
|
||||
|
||||
diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c
|
||||
index 3c337c7..76c1f7f 100644
|
||||
--- a/drivers/hwmon/max6620.c
|
||||
+++ b/drivers/hwmon/max6620.c
|
||||
@@ -46,6 +46,8 @@
|
||||
|
||||
/* clock: The clock frequency of the chip the driver should assume */
|
||||
static int clock = 8192;
|
||||
+static u32 sr = 2;
|
||||
+static u32 np = 2;
|
||||
|
||||
module_param(clock, int, S_IRUGO);
|
||||
|
||||
@@ -213,22 +215,22 @@ static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, cha
|
||||
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max6620_data *data = max6620_update_device(dev);
|
||||
- int rpm;
|
||||
-
|
||||
- /*
|
||||
- * Calculation details:
|
||||
- *
|
||||
- * Each tachometer counts over an interval given by the "count"
|
||||
- * register (0.25, 0.5, 1 or 2 seconds). This module assumes
|
||||
- * that the fans produce two pulses per revolution (this seems
|
||||
- * to be the most common).
|
||||
- */
|
||||
- if(data->tach[attr->index] == 0 || data->tach[attr->index] == 255) {
|
||||
+ struct i2c_client *client = to_i2c_client(dev);
|
||||
+ u32 rpm = 0;
|
||||
+ u32 tach = 0;
|
||||
+ u32 tach1 = 0;
|
||||
+ u32 tach2 = 0;
|
||||
+
|
||||
+ tach1 = i2c_smbus_read_byte_data(client, tach_reg[attr->index]);
|
||||
+ tach1 = (tach1 << 3) & 0x7f8;
|
||||
+ tach2 = i2c_smbus_read_byte_data(client, tach_reg[attr->index] + 1);
|
||||
+ tach2 = (tach2 >> 5) & 0x7;
|
||||
+ tach = tach1 | tach2;
|
||||
+ if (tach == 0) {
|
||||
rpm = 0;
|
||||
} else {
|
||||
- rpm = ((clock / (data->tach[attr->index] << 3)) * 30 * DIV_FROM_REG(data->fandyn[attr->index]));
|
||||
+ rpm = (60 * sr * clock)/(tach * np);
|
||||
}
|
||||
-
|
||||
return sprintf(buf, "%d\n", rpm);
|
||||
}
|
||||
|
||||
@@ -236,22 +238,21 @@ static ssize_t get_target(struct device *dev, struct device_attribute *devattr,
|
||||
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max6620_data *data = max6620_update_device(dev);
|
||||
- int kscale, ktach, rpm;
|
||||
-
|
||||
- /*
|
||||
- * Use the datasheet equation:
|
||||
- *
|
||||
- * FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)]
|
||||
- *
|
||||
- * then multiply by 60 to give rpm.
|
||||
- */
|
||||
-
|
||||
- kscale = DIV_FROM_REG(data->fandyn[attr->index]);
|
||||
- ktach = data->target[attr->index];
|
||||
- if(ktach == 0) {
|
||||
+ struct i2c_client *client = to_i2c_client(dev);
|
||||
+ u32 rpm;
|
||||
+ u32 target;
|
||||
+ u32 target1;
|
||||
+ u32 target2;
|
||||
+
|
||||
+ target1 = i2c_smbus_read_byte_data(client, target_reg[attr->index]);
|
||||
+ target1 = (target1 << 3) & 0x7f8;
|
||||
+ target2 = i2c_smbus_read_byte_data(client, target_reg[attr->index] + 1);
|
||||
+ target2 = (target2 >> 5) & 0x7;
|
||||
+ target = target1 | target2;
|
||||
+ if (target == 0) {
|
||||
rpm = 0;
|
||||
} else {
|
||||
- rpm = ((60 * kscale * clock) / (ktach << 3));
|
||||
+ rpm = (60 * sr * clock)/(target * np);
|
||||
}
|
||||
return sprintf(buf, "%d\n", rpm);
|
||||
}
|
||||
@@ -261,9 +262,11 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max6620_data *data = i2c_get_clientdata(client);
|
||||
- int kscale, ktach;
|
||||
- unsigned long rpm;
|
||||
+ u32 rpm;
|
||||
int err;
|
||||
+ u32 target;
|
||||
+ u32 target1;
|
||||
+ u32 target2;
|
||||
|
||||
err = kstrtoul(buf, 10, &rpm);
|
||||
if (err)
|
||||
@@ -271,25 +274,13 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
|
||||
|
||||
rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
|
||||
|
||||
- /*
|
||||
- * Divide the required speed by 60 to get from rpm to rps, then
|
||||
- * use the datasheet equation:
|
||||
- *
|
||||
- * KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1
|
||||
- */
|
||||
-
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
- kscale = DIV_FROM_REG(data->fandyn[attr->index]);
|
||||
- ktach = ((60 * kscale * clock) / rpm);
|
||||
- if (ktach < 0)
|
||||
- ktach = 0;
|
||||
- if (ktach > 255)
|
||||
- ktach = 255;
|
||||
- data->target[attr->index] = ktach;
|
||||
-
|
||||
- i2c_smbus_write_byte_data(client, target_reg[attr->index], data->target[attr->index]);
|
||||
- i2c_smbus_write_byte_data(client, target_reg[attr->index]+0x01, 0x00);
|
||||
+ target = (60 * sr * 8192)/(rpm * np);
|
||||
+ target1 = (target >> 3) & 0xff;
|
||||
+ target2 = (target << 5) & 0xe0;
|
||||
+ i2c_smbus_write_byte_data(client, target_reg[attr->index], target1);
|
||||
+ i2c_smbus_write_byte_data(client, target_reg[attr->index] + 1, target2);
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
@@ -609,8 +600,11 @@ static int max6620_init_client(struct i2c_client *client) {
|
||||
}
|
||||
|
||||
|
||||
-
|
||||
- if (i2c_smbus_write_byte_data(client, MAX6620_REG_CONFIG, config)) {
|
||||
+ /*
|
||||
+ * Set bit 4, disable other fans from going full speed on a fail
|
||||
+ * failure.
|
||||
+ */
|
||||
+ if (i2c_smbus_write_byte_data(client, MAX6620_REG_CONFIG, config | 0x10)) {
|
||||
dev_err(&client->dev, "Config write error, aborting.\n");
|
||||
return err;
|
||||
}
|
||||
@@ -618,28 +612,21 @@ static int max6620_init_client(struct i2c_client *client) {
|
||||
data->config = config;
|
||||
for (i = 0; i < 4; i++) {
|
||||
data->fancfg[i] = i2c_smbus_read_byte_data(client, config_reg[i]);
|
||||
- data->fancfg[i] |= 0x80; // enable TACH monitoring
|
||||
+ data->fancfg[i] |= 0xa8; // enable TACH monitoring
|
||||
i2c_smbus_write_byte_data(client, config_reg[i], data->fancfg[i]);
|
||||
data->fandyn[i] = i2c_smbus_read_byte_data(client, dyn_reg[i]);
|
||||
- data-> fandyn[i] |= 0x1C;
|
||||
+ /* 2 counts (001) and Rate change 100 (0.125 secs) */
|
||||
+ data-> fandyn[i] = 0x30;
|
||||
i2c_smbus_write_byte_data(client, dyn_reg[i], data->fandyn[i]);
|
||||
data->tach[i] = i2c_smbus_read_byte_data(client, tach_reg[i]);
|
||||
data->volt[i] = i2c_smbus_read_byte_data(client, volt_reg[i]);
|
||||
data->target[i] = i2c_smbus_read_byte_data(client, target_reg[i]);
|
||||
data->dac[i] = i2c_smbus_read_byte_data(client, dac_reg[i]);
|
||||
|
||||
-
|
||||
-
|
||||
}
|
||||
-
|
||||
-
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
-
|
||||
-
|
||||
-
|
||||
static struct max6620_data *max6620_update_device(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
@@ -678,7 +665,7 @@ static struct max6620_data *max6620_update_device(struct device *dev)
|
||||
return data;
|
||||
}
|
||||
|
||||
-module_i2c_driver(max6620_driver);
|
||||
+// module_i2c_driver(max6620_driver);
|
||||
|
||||
static int __init max6620_init(void)
|
||||
{
|
||||
@@ -0,0 +1,113 @@
|
||||
Update MAX6620 driver to support newer kernel version
|
||||
|
||||
From: Shuotian Cheng <shuche@microsoft.com>
|
||||
|
||||
|
||||
---
|
||||
drivers/hwmon/max6620.c | 25 +++++++++++--------------
|
||||
1 file changed, 11 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c
|
||||
index 76c1f7f..fb49195 100644
|
||||
--- a/drivers/hwmon/max6620.c
|
||||
+++ b/drivers/hwmon/max6620.c
|
||||
@@ -183,7 +183,7 @@ static struct i2c_driver max6620_driver = {
|
||||
.name = "max6620",
|
||||
},
|
||||
.probe = max6620_probe,
|
||||
- .remove = __devexit_p(max6620_remove),
|
||||
+ .remove = max6620_remove,
|
||||
.id_table = max6620_id,
|
||||
.address_list = normal_i2c,
|
||||
};
|
||||
@@ -231,6 +231,7 @@ static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, cha
|
||||
} else {
|
||||
rpm = (60 * sr * clock)/(tach * np);
|
||||
}
|
||||
+
|
||||
return sprintf(buf, "%d\n", rpm);
|
||||
}
|
||||
|
||||
@@ -262,17 +263,17 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max6620_data *data = i2c_get_clientdata(client);
|
||||
- u32 rpm;
|
||||
+ unsigned long rpm;
|
||||
int err;
|
||||
- u32 target;
|
||||
- u32 target1;
|
||||
- u32 target2;
|
||||
+ unsigned long target;
|
||||
+ unsigned long target1;
|
||||
+ unsigned long target2;
|
||||
|
||||
err = kstrtoul(buf, 10, &rpm);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
- rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
|
||||
+ rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
@@ -326,7 +327,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, con
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
- pwm = SENSORS_LIMIT(pwm, 0, 255);
|
||||
+ pwm = clamp_val(pwm, 0, 255);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
@@ -534,7 +535,7 @@ static struct attribute_group max6620_attr_grp = {
|
||||
* Real code
|
||||
*/
|
||||
|
||||
-static int __devinit max6620_probe(struct i2c_client *client, const struct i2c_device_id *id) {
|
||||
+static int max6620_probe(struct i2c_client *client, const struct i2c_device_id *id) {
|
||||
|
||||
struct max6620_data *data;
|
||||
int err;
|
||||
@@ -575,7 +576,7 @@ dev_info(&client->dev, "Sysfs entries created\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
-static int __devexit max6620_remove(struct i2c_client *client) {
|
||||
+static int max6620_remove(struct i2c_client *client) {
|
||||
|
||||
struct max6620_data *data = i2c_get_clientdata(client);
|
||||
|
||||
@@ -599,7 +600,6 @@ static int max6620_init_client(struct i2c_client *client) {
|
||||
return err;
|
||||
}
|
||||
|
||||
-
|
||||
/*
|
||||
* Set bit 4, disable other fans from going full speed on a fail
|
||||
* failure.
|
||||
@@ -615,14 +615,13 @@ static int max6620_init_client(struct i2c_client *client) {
|
||||
data->fancfg[i] |= 0xa8; // enable TACH monitoring
|
||||
i2c_smbus_write_byte_data(client, config_reg[i], data->fancfg[i]);
|
||||
data->fandyn[i] = i2c_smbus_read_byte_data(client, dyn_reg[i]);
|
||||
- /* 2 counts (001) and Rate change 100 (0.125 secs) */
|
||||
+ /* 2 counts (001) and Rate change 100 (0.125 secs) */
|
||||
data-> fandyn[i] = 0x30;
|
||||
i2c_smbus_write_byte_data(client, dyn_reg[i], data->fandyn[i]);
|
||||
data->tach[i] = i2c_smbus_read_byte_data(client, tach_reg[i]);
|
||||
data->volt[i] = i2c_smbus_read_byte_data(client, volt_reg[i]);
|
||||
data->target[i] = i2c_smbus_read_byte_data(client, target_reg[i]);
|
||||
data->dac[i] = i2c_smbus_read_byte_data(client, dac_reg[i]);
|
||||
-
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -665,8 +664,6 @@ static struct max6620_data *max6620_update_device(struct device *dev)
|
||||
return data;
|
||||
}
|
||||
|
||||
-// module_i2c_driver(max6620_driver);
|
||||
-
|
||||
static int __init max6620_init(void)
|
||||
{
|
||||
return i2c_add_driver(&max6620_driver);
|
||||
@@ -0,0 +1,753 @@
|
||||
Driver for MAX6620 Fan sensor
|
||||
|
||||
From: Cumulus Networks <support@cumulusnetworks.com>
|
||||
|
||||
|
||||
---
|
||||
drivers/hwmon/Kconfig | 10 +
|
||||
drivers/hwmon/Makefile | 1
|
||||
drivers/hwmon/max6620.c | 702 +++++++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 713 insertions(+)
|
||||
create mode 100644 drivers/hwmon/max6620.c
|
||||
|
||||
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
|
||||
index 02d3d85..ca38e05 100644
|
||||
--- a/drivers/hwmon/Kconfig
|
||||
+++ b/drivers/hwmon/Kconfig
|
||||
@@ -784,6 +784,16 @@ config SENSORS_MAX6650
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called max6650.
|
||||
|
||||
+config SENSORS_MAX6620
|
||||
+ tristate "Maxim MAX6620 sensor chip"
|
||||
+ depends on I2C
|
||||
+ help
|
||||
+ If you say yes here you get support for the MAX6620
|
||||
+ sensor chips.
|
||||
+
|
||||
+ This driver can also be built as a module. If so, the module
|
||||
+ will be called max6620.
|
||||
+
|
||||
config SENSORS_MAX6697
|
||||
tristate "Maxim MAX6697 and compatibles"
|
||||
depends on I2C
|
||||
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
|
||||
index 3dc0f02..8837a7b 100644
|
||||
--- a/drivers/hwmon/Makefile
|
||||
+++ b/drivers/hwmon/Makefile
|
||||
@@ -111,6 +111,7 @@ obj-$(CONFIG_SENSORS_MAX197) += max197.o
|
||||
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
|
||||
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
|
||||
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
|
||||
+obj-$(CONFIG_SENSORS_MAX6620) += max6620.o
|
||||
obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
|
||||
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
|
||||
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
|
||||
diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c
|
||||
new file mode 100644
|
||||
index 0000000..3c337c7
|
||||
--- /dev/null
|
||||
+++ b/drivers/hwmon/max6620.c
|
||||
@@ -0,0 +1,702 @@
|
||||
+/*
|
||||
+ * max6620.c - Linux Kernel module for hardware monitoring.
|
||||
+ *
|
||||
+ * (C) 2012 by L. Grunenberg <contact@lgrunenberg.de>
|
||||
+ *
|
||||
+ * based on code written by :
|
||||
+ * 2007 by Hans J. Koch <hjk@hansjkoch.de>
|
||||
+ * John Morris <john.morris@spirentcom.com>
|
||||
+ * Copyright (c) 2003 Spirent Communications
|
||||
+ * and Claus Gindhart <claus.gindhart@kontron.com>
|
||||
+ *
|
||||
+ * This module has only been tested with the MAX6620 chip.
|
||||
+ *
|
||||
+ * The datasheet was last seen at:
|
||||
+ *
|
||||
+ * http://pdfserv.maxim-ic.com/en/ds/MAX6620.pdf
|
||||
+ *
|
||||
+ * 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/err.h>
|
||||
+
|
||||
+/*
|
||||
+ * Insmod parameters
|
||||
+ */
|
||||
+
|
||||
+
|
||||
+/* clock: The clock frequency of the chip the driver should assume */
|
||||
+static int clock = 8192;
|
||||
+
|
||||
+module_param(clock, int, S_IRUGO);
|
||||
+
|
||||
+static const unsigned short normal_i2c[] = {0x0a, 0x1a, 0x2a, I2C_CLIENT_END};
|
||||
+
|
||||
+/*
|
||||
+ * MAX 6620 registers
|
||||
+ */
|
||||
+
|
||||
+#define MAX6620_REG_CONFIG 0x00
|
||||
+#define MAX6620_REG_FAULT 0x01
|
||||
+#define MAX6620_REG_CONF_FAN0 0x02
|
||||
+#define MAX6620_REG_CONF_FAN1 0x03
|
||||
+#define MAX6620_REG_CONF_FAN2 0x04
|
||||
+#define MAX6620_REG_CONF_FAN3 0x05
|
||||
+#define MAX6620_REG_DYN_FAN0 0x06
|
||||
+#define MAX6620_REG_DYN_FAN1 0x07
|
||||
+#define MAX6620_REG_DYN_FAN2 0x08
|
||||
+#define MAX6620_REG_DYN_FAN3 0x09
|
||||
+#define MAX6620_REG_TACH0 0x10
|
||||
+#define MAX6620_REG_TACH1 0x12
|
||||
+#define MAX6620_REG_TACH2 0x14
|
||||
+#define MAX6620_REG_TACH3 0x16
|
||||
+#define MAX6620_REG_VOLT0 0x18
|
||||
+#define MAX6620_REG_VOLT1 0x1A
|
||||
+#define MAX6620_REG_VOLT2 0x1C
|
||||
+#define MAX6620_REG_VOLT3 0x1E
|
||||
+#define MAX6620_REG_TAR0 0x20
|
||||
+#define MAX6620_REG_TAR1 0x22
|
||||
+#define MAX6620_REG_TAR2 0x24
|
||||
+#define MAX6620_REG_TAR3 0x26
|
||||
+#define MAX6620_REG_DAC0 0x28
|
||||
+#define MAX6620_REG_DAC1 0x2A
|
||||
+#define MAX6620_REG_DAC2 0x2C
|
||||
+#define MAX6620_REG_DAC3 0x2E
|
||||
+
|
||||
+/*
|
||||
+ * Config register bits
|
||||
+ */
|
||||
+
|
||||
+#define MAX6620_CFG_RUN 0x80
|
||||
+#define MAX6620_CFG_POR 0x40
|
||||
+#define MAX6620_CFG_TIMEOUT 0x20
|
||||
+#define MAX6620_CFG_FULLFAN 0x10
|
||||
+#define MAX6620_CFG_OSC 0x08
|
||||
+#define MAX6620_CFG_WD_MASK 0x06
|
||||
+#define MAX6620_CFG_WD_2 0x02
|
||||
+#define MAX6620_CFG_WD_6 0x04
|
||||
+#define MAX6620_CFG_WD10 0x06
|
||||
+#define MAX6620_CFG_WD 0x01
|
||||
+
|
||||
+
|
||||
+/*
|
||||
+ * Failure status register bits
|
||||
+ */
|
||||
+
|
||||
+#define MAX6620_FAIL_TACH0 0x10
|
||||
+#define MAX6620_FAIL_TACH1 0x20
|
||||
+#define MAX6620_FAIL_TACH2 0x40
|
||||
+#define MAX6620_FAIL_TACH3 0x80
|
||||
+#define MAX6620_FAIL_MASK0 0x01
|
||||
+#define MAX6620_FAIL_MASK1 0x02
|
||||
+#define MAX6620_FAIL_MASK2 0x04
|
||||
+#define MAX6620_FAIL_MASK3 0x08
|
||||
+
|
||||
+
|
||||
+/* Minimum and maximum values of the FAN-RPM */
|
||||
+#define FAN_RPM_MIN 240
|
||||
+#define FAN_RPM_MAX 30000
|
||||
+
|
||||
+#define DIV_FROM_REG(reg) (1 << ((reg & 0xE0) >> 5))
|
||||
+
|
||||
+static int max6620_probe(struct i2c_client *client, const struct i2c_device_id *id);
|
||||
+static int max6620_init_client(struct i2c_client *client);
|
||||
+static int max6620_remove(struct i2c_client *client);
|
||||
+static struct max6620_data *max6620_update_device(struct device *dev);
|
||||
+
|
||||
+static const u8 config_reg[] = {
|
||||
+ MAX6620_REG_CONF_FAN0,
|
||||
+ MAX6620_REG_CONF_FAN1,
|
||||
+ MAX6620_REG_CONF_FAN2,
|
||||
+ MAX6620_REG_CONF_FAN3,
|
||||
+};
|
||||
+
|
||||
+static const u8 dyn_reg[] = {
|
||||
+ MAX6620_REG_DYN_FAN0,
|
||||
+ MAX6620_REG_DYN_FAN1,
|
||||
+ MAX6620_REG_DYN_FAN2,
|
||||
+ MAX6620_REG_DYN_FAN3,
|
||||
+};
|
||||
+
|
||||
+static const u8 tach_reg[] = {
|
||||
+ MAX6620_REG_TACH0,
|
||||
+ MAX6620_REG_TACH1,
|
||||
+ MAX6620_REG_TACH2,
|
||||
+ MAX6620_REG_TACH3,
|
||||
+};
|
||||
+
|
||||
+static const u8 volt_reg[] = {
|
||||
+ MAX6620_REG_VOLT0,
|
||||
+ MAX6620_REG_VOLT1,
|
||||
+ MAX6620_REG_VOLT2,
|
||||
+ MAX6620_REG_VOLT3,
|
||||
+};
|
||||
+
|
||||
+static const u8 target_reg[] = {
|
||||
+ MAX6620_REG_TAR0,
|
||||
+ MAX6620_REG_TAR1,
|
||||
+ MAX6620_REG_TAR2,
|
||||
+ MAX6620_REG_TAR3,
|
||||
+};
|
||||
+
|
||||
+static const u8 dac_reg[] = {
|
||||
+ MAX6620_REG_DAC0,
|
||||
+ MAX6620_REG_DAC1,
|
||||
+ MAX6620_REG_DAC2,
|
||||
+ MAX6620_REG_DAC3,
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * Driver data (common to all clients)
|
||||
+ */
|
||||
+
|
||||
+static const struct i2c_device_id max6620_id[] = {
|
||||
+ { "max6620", 0 },
|
||||
+ { }
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(i2c, max6620_id);
|
||||
+
|
||||
+static struct i2c_driver max6620_driver = {
|
||||
+ .class = I2C_CLASS_HWMON,
|
||||
+ .driver = {
|
||||
+ .name = "max6620",
|
||||
+ },
|
||||
+ .probe = max6620_probe,
|
||||
+ .remove = __devexit_p(max6620_remove),
|
||||
+ .id_table = max6620_id,
|
||||
+ .address_list = normal_i2c,
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * Client data (each client gets its own)
|
||||
+ */
|
||||
+
|
||||
+struct max6620_data {
|
||||
+ struct device *hwmon_dev;
|
||||
+ struct mutex update_lock;
|
||||
+ int nr_fans;
|
||||
+ char valid; /* zero until following fields are valid */
|
||||
+ unsigned long last_updated; /* in jiffies */
|
||||
+
|
||||
+ /* register values */
|
||||
+ u8 speed[4];
|
||||
+ u8 config;
|
||||
+ u8 fancfg[4];
|
||||
+ u8 fandyn[4];
|
||||
+ u8 tach[4];
|
||||
+ u8 volt[4];
|
||||
+ u8 target[4];
|
||||
+ u8 dac[4];
|
||||
+ u8 fault;
|
||||
+};
|
||||
+
|
||||
+static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, char *buf) {
|
||||
+
|
||||
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
+ struct max6620_data *data = max6620_update_device(dev);
|
||||
+ int rpm;
|
||||
+
|
||||
+ /*
|
||||
+ * Calculation details:
|
||||
+ *
|
||||
+ * Each tachometer counts over an interval given by the "count"
|
||||
+ * register (0.25, 0.5, 1 or 2 seconds). This module assumes
|
||||
+ * that the fans produce two pulses per revolution (this seems
|
||||
+ * to be the most common).
|
||||
+ */
|
||||
+ if(data->tach[attr->index] == 0 || data->tach[attr->index] == 255) {
|
||||
+ rpm = 0;
|
||||
+ } else {
|
||||
+ rpm = ((clock / (data->tach[attr->index] << 3)) * 30 * DIV_FROM_REG(data->fandyn[attr->index]));
|
||||
+ }
|
||||
+
|
||||
+ return sprintf(buf, "%d\n", rpm);
|
||||
+}
|
||||
+
|
||||
+static ssize_t get_target(struct device *dev, struct device_attribute *devattr, char *buf) {
|
||||
+
|
||||
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
+ struct max6620_data *data = max6620_update_device(dev);
|
||||
+ int kscale, ktach, rpm;
|
||||
+
|
||||
+ /*
|
||||
+ * Use the datasheet equation:
|
||||
+ *
|
||||
+ * FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)]
|
||||
+ *
|
||||
+ * then multiply by 60 to give rpm.
|
||||
+ */
|
||||
+
|
||||
+ kscale = DIV_FROM_REG(data->fandyn[attr->index]);
|
||||
+ ktach = data->target[attr->index];
|
||||
+ if(ktach == 0) {
|
||||
+ rpm = 0;
|
||||
+ } else {
|
||||
+ rpm = ((60 * kscale * clock) / (ktach << 3));
|
||||
+ }
|
||||
+ return sprintf(buf, "%d\n", rpm);
|
||||
+}
|
||||
+
|
||||
+static ssize_t set_target(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) {
|
||||
+
|
||||
+ struct i2c_client *client = to_i2c_client(dev);
|
||||
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
+ struct max6620_data *data = i2c_get_clientdata(client);
|
||||
+ int kscale, ktach;
|
||||
+ unsigned long rpm;
|
||||
+ int err;
|
||||
+
|
||||
+ err = kstrtoul(buf, 10, &rpm);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
|
||||
+
|
||||
+ /*
|
||||
+ * Divide the required speed by 60 to get from rpm to rps, then
|
||||
+ * use the datasheet equation:
|
||||
+ *
|
||||
+ * KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1
|
||||
+ */
|
||||
+
|
||||
+ mutex_lock(&data->update_lock);
|
||||
+
|
||||
+ kscale = DIV_FROM_REG(data->fandyn[attr->index]);
|
||||
+ ktach = ((60 * kscale * clock) / rpm);
|
||||
+ if (ktach < 0)
|
||||
+ ktach = 0;
|
||||
+ if (ktach > 255)
|
||||
+ ktach = 255;
|
||||
+ data->target[attr->index] = ktach;
|
||||
+
|
||||
+ i2c_smbus_write_byte_data(client, target_reg[attr->index], data->target[attr->index]);
|
||||
+ i2c_smbus_write_byte_data(client, target_reg[attr->index]+0x01, 0x00);
|
||||
+
|
||||
+ mutex_unlock(&data->update_lock);
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Get/set the fan speed in open loop mode using pwm1 sysfs file.
|
||||
+ * Speed is given as a relative value from 0 to 255, where 255 is maximum
|
||||
+ * speed. Note that this is done by writing directly to the chip's DAC,
|
||||
+ * it won't change the closed loop speed set by fan1_target.
|
||||
+ * Also note that due to rounding errors it is possible that you don't read
|
||||
+ * back exactly the value you have set.
|
||||
+ */
|
||||
+
|
||||
+static ssize_t get_pwm(struct device *dev, struct device_attribute *devattr, char *buf) {
|
||||
+
|
||||
+ int pwm;
|
||||
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
+ struct max6620_data *data = max6620_update_device(dev);
|
||||
+
|
||||
+ /*
|
||||
+ * Useful range for dac is 0-180 for 12V fans and 0-76 for 5V fans.
|
||||
+ * Lower DAC values mean higher speeds.
|
||||
+ */
|
||||
+ pwm = ((int)data->volt[attr->index]);
|
||||
+
|
||||
+ if (pwm < 0)
|
||||
+ pwm = 0;
|
||||
+
|
||||
+ return sprintf(buf, "%d\n", pwm);
|
||||
+}
|
||||
+
|
||||
+static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) {
|
||||
+
|
||||
+ struct i2c_client *client = to_i2c_client(dev);
|
||||
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
+ struct max6620_data *data = i2c_get_clientdata(client);
|
||||
+ unsigned long pwm;
|
||||
+ int err;
|
||||
+
|
||||
+ err = kstrtoul(buf, 10, &pwm);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ pwm = SENSORS_LIMIT(pwm, 0, 255);
|
||||
+
|
||||
+ mutex_lock(&data->update_lock);
|
||||
+
|
||||
+ data->dac[attr->index] = pwm;
|
||||
+
|
||||
+
|
||||
+ i2c_smbus_write_byte_data(client, dac_reg[attr->index], data->dac[attr->index]);
|
||||
+ i2c_smbus_write_byte_data(client, dac_reg[attr->index]+1, 0x00);
|
||||
+
|
||||
+ mutex_unlock(&data->update_lock);
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Get/Set controller mode:
|
||||
+ * Possible values:
|
||||
+ * 0 = Fan always on
|
||||
+ * 1 = Open loop, Voltage is set according to speed, not regulated.
|
||||
+ * 2 = Closed loop, RPM for all fans regulated by fan1 tachometer
|
||||
+ */
|
||||
+
|
||||
+static ssize_t get_enable(struct device *dev, struct device_attribute *devattr, char *buf) {
|
||||
+
|
||||
+ struct max6620_data *data = max6620_update_device(dev);
|
||||
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
+ int mode = (data->fancfg[attr->index] & 0x80 ) >> 7;
|
||||
+ int sysfs_modes[2] = {1, 2};
|
||||
+
|
||||
+ return sprintf(buf, "%d\n", sysfs_modes[mode]);
|
||||
+}
|
||||
+
|
||||
+static ssize_t set_enable(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) {
|
||||
+
|
||||
+ struct i2c_client *client = to_i2c_client(dev);
|
||||
+ struct max6620_data *data = i2c_get_clientdata(client);
|
||||
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
+ int max6620_modes[3] = {0, 1, 0};
|
||||
+ unsigned long mode;
|
||||
+ int err;
|
||||
+
|
||||
+ err = kstrtoul(buf, 10, &mode);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ if (mode > 2)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ mutex_lock(&data->update_lock);
|
||||
+
|
||||
+ data->fancfg[attr->index] = i2c_smbus_read_byte_data(client, config_reg[attr->index]);
|
||||
+ data->fancfg[attr->index] = (data->fancfg[attr->index] & ~0x80)
|
||||
+ | (max6620_modes[mode] << 7);
|
||||
+
|
||||
+ i2c_smbus_write_byte_data(client, config_reg[attr->index], data->fancfg[attr->index]);
|
||||
+
|
||||
+ mutex_unlock(&data->update_lock);
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Read/write functions for fan1_div sysfs file. The MAX6620 has no such
|
||||
+ * divider. We handle this by converting between divider and counttime:
|
||||
+ *
|
||||
+ * (counttime == k) <==> (divider == 2^k), k = 0, 1, 2, 3, 4 or 5
|
||||
+ *
|
||||
+ * Lower values of k allow to connect a faster fan without the risk of
|
||||
+ * counter overflow. The price is lower resolution. You can also set counttime
|
||||
+ * using the module parameter. Note that the module parameter "prescaler" also
|
||||
+ * influences the behaviour. Unfortunately, there's no sysfs attribute
|
||||
+ * defined for that. See the data sheet for details.
|
||||
+ */
|
||||
+
|
||||
+static ssize_t get_div(struct device *dev, struct device_attribute *devattr, char *buf) {
|
||||
+
|
||||
+ struct max6620_data *data = max6620_update_device(dev);
|
||||
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
+
|
||||
+ return sprintf(buf, "%d\n", DIV_FROM_REG(data->fandyn[attr->index]));
|
||||
+}
|
||||
+
|
||||
+static ssize_t set_div(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) {
|
||||
+
|
||||
+ struct i2c_client *client = to_i2c_client(dev);
|
||||
+ struct max6620_data *data = i2c_get_clientdata(client);
|
||||
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
+ unsigned long div;
|
||||
+ int err;
|
||||
+ u8 div_bin;
|
||||
+
|
||||
+ err = kstrtoul(buf, 10, &div);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ mutex_lock(&data->update_lock);
|
||||
+ switch (div) {
|
||||
+ case 1:
|
||||
+ div_bin = 0;
|
||||
+ break;
|
||||
+ case 2:
|
||||
+ div_bin = 1;
|
||||
+ break;
|
||||
+ case 4:
|
||||
+ div_bin = 2;
|
||||
+ break;
|
||||
+ case 8:
|
||||
+ div_bin = 3;
|
||||
+ break;
|
||||
+ case 16:
|
||||
+ div_bin = 4;
|
||||
+ break;
|
||||
+ case 32:
|
||||
+ div_bin = 5;
|
||||
+ break;
|
||||
+ default:
|
||||
+ mutex_unlock(&data->update_lock);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ data->fandyn[attr->index] &= 0x1F;
|
||||
+ data->fandyn[attr->index] |= div_bin << 5;
|
||||
+ i2c_smbus_write_byte_data(client, dyn_reg[attr->index], data->fandyn[attr->index]);
|
||||
+ mutex_unlock(&data->update_lock);
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Get alarm stati:
|
||||
+ * Possible values:
|
||||
+ * 0 = no alarm
|
||||
+ * 1 = alarm
|
||||
+ */
|
||||
+
|
||||
+static ssize_t get_alarm(struct device *dev, struct device_attribute *devattr, char *buf) {
|
||||
+
|
||||
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
+ struct max6620_data *data = max6620_update_device(dev);
|
||||
+ struct i2c_client *client = to_i2c_client(dev);
|
||||
+ int alarm = 0;
|
||||
+
|
||||
+ if (data->fault & (1 << attr->index)) {
|
||||
+ mutex_lock(&data->update_lock);
|
||||
+ alarm = 1;
|
||||
+ data->fault &= ~(1 << attr->index);
|
||||
+ data->fault |= i2c_smbus_read_byte_data(client,
|
||||
+ MAX6620_REG_FAULT);
|
||||
+ mutex_unlock(&data->update_lock);
|
||||
+ }
|
||||
+
|
||||
+ return sprintf(buf, "%d\n", alarm);
|
||||
+}
|
||||
+
|
||||
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0);
|
||||
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1);
|
||||
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2);
|
||||
+static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3);
|
||||
+static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, get_target, set_target, 0);
|
||||
+static SENSOR_DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, get_div, set_div, 0);
|
||||
+// static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, 0);
|
||||
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 0);
|
||||
+static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO, get_target, set_target, 1);
|
||||
+static SENSOR_DEVICE_ATTR(fan2_div, S_IWUSR | S_IRUGO, get_div, set_div, 1);
|
||||
+// static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, 1);
|
||||
+static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 1);
|
||||
+static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO, get_target, set_target, 2);
|
||||
+static SENSOR_DEVICE_ATTR(fan3_div, S_IWUSR | S_IRUGO, get_div, set_div, 2);
|
||||
+// static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, 2);
|
||||
+static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 2);
|
||||
+static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO, get_target, set_target, 3);
|
||||
+static SENSOR_DEVICE_ATTR(fan4_div, S_IWUSR | S_IRUGO, get_div, set_div, 3);
|
||||
+// static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, 3);
|
||||
+static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 3);
|
||||
+
|
||||
+static struct attribute *max6620_attrs[] = {
|
||||
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
|
||||
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
|
||||
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
|
||||
+ &sensor_dev_attr_fan1_target.dev_attr.attr,
|
||||
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
|
||||
+// &sensor_dev_attr_pwm1_enable.dev_attr.attr,
|
||||
+ &sensor_dev_attr_pwm1.dev_attr.attr,
|
||||
+ &sensor_dev_attr_fan2_target.dev_attr.attr,
|
||||
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
|
||||
+// &sensor_dev_attr_pwm2_enable.dev_attr.attr,
|
||||
+ &sensor_dev_attr_pwm2.dev_attr.attr,
|
||||
+ &sensor_dev_attr_fan3_target.dev_attr.attr,
|
||||
+ &sensor_dev_attr_fan3_div.dev_attr.attr,
|
||||
+// &sensor_dev_attr_pwm3_enable.dev_attr.attr,
|
||||
+ &sensor_dev_attr_pwm3.dev_attr.attr,
|
||||
+ &sensor_dev_attr_fan4_target.dev_attr.attr,
|
||||
+ &sensor_dev_attr_fan4_div.dev_attr.attr,
|
||||
+// &sensor_dev_attr_pwm4_enable.dev_attr.attr,
|
||||
+ &sensor_dev_attr_pwm4.dev_attr.attr,
|
||||
+ NULL
|
||||
+};
|
||||
+
|
||||
+static struct attribute_group max6620_attr_grp = {
|
||||
+ .attrs = max6620_attrs,
|
||||
+};
|
||||
+
|
||||
+
|
||||
+/*
|
||||
+ * Real code
|
||||
+ */
|
||||
+
|
||||
+static int __devinit max6620_probe(struct i2c_client *client, const struct i2c_device_id *id) {
|
||||
+
|
||||
+ struct max6620_data *data;
|
||||
+ int err;
|
||||
+
|
||||
+ data = devm_kzalloc(&client->dev, sizeof(struct max6620_data), GFP_KERNEL);
|
||||
+ if (!data) {
|
||||
+ dev_err(&client->dev, "out of memory.\n");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+
|
||||
+ i2c_set_clientdata(client, data);
|
||||
+ mutex_init(&data->update_lock);
|
||||
+ data->nr_fans = id->driver_data;
|
||||
+
|
||||
+ /*
|
||||
+ * Initialize the max6620 chip
|
||||
+ */
|
||||
+ dev_info(&client->dev, "About to initialize module\n");
|
||||
+
|
||||
+ err = max6620_init_client(client);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+ dev_info(&client->dev, "Module initialized\n");
|
||||
+
|
||||
+ err = sysfs_create_group(&client->dev.kobj, &max6620_attr_grp);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+dev_info(&client->dev, "Sysfs entries created\n");
|
||||
+
|
||||
+ data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
+ if (!IS_ERR(data->hwmon_dev))
|
||||
+ return 0;
|
||||
+
|
||||
+ err = PTR_ERR(data->hwmon_dev);
|
||||
+ dev_err(&client->dev, "error registering hwmon device.\n");
|
||||
+
|
||||
+ sysfs_remove_group(&client->dev.kobj, &max6620_attr_grp);
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+static int __devexit max6620_remove(struct i2c_client *client) {
|
||||
+
|
||||
+ struct max6620_data *data = i2c_get_clientdata(client);
|
||||
+
|
||||
+ hwmon_device_unregister(data->hwmon_dev);
|
||||
+
|
||||
+ sysfs_remove_group(&client->dev.kobj, &max6620_attr_grp);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int max6620_init_client(struct i2c_client *client) {
|
||||
+
|
||||
+ struct max6620_data *data = i2c_get_clientdata(client);
|
||||
+ int config;
|
||||
+ int err = -EIO;
|
||||
+ int i;
|
||||
+
|
||||
+ config = i2c_smbus_read_byte_data(client, MAX6620_REG_CONFIG);
|
||||
+
|
||||
+ if (config < 0) {
|
||||
+ dev_err(&client->dev, "Error reading config, aborting.\n");
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+
|
||||
+ if (i2c_smbus_write_byte_data(client, MAX6620_REG_CONFIG, config)) {
|
||||
+ dev_err(&client->dev, "Config write error, aborting.\n");
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ data->config = config;
|
||||
+ for (i = 0; i < 4; i++) {
|
||||
+ data->fancfg[i] = i2c_smbus_read_byte_data(client, config_reg[i]);
|
||||
+ data->fancfg[i] |= 0x80; // enable TACH monitoring
|
||||
+ i2c_smbus_write_byte_data(client, config_reg[i], data->fancfg[i]);
|
||||
+ data->fandyn[i] = i2c_smbus_read_byte_data(client, dyn_reg[i]);
|
||||
+ data-> fandyn[i] |= 0x1C;
|
||||
+ i2c_smbus_write_byte_data(client, dyn_reg[i], data->fandyn[i]);
|
||||
+ data->tach[i] = i2c_smbus_read_byte_data(client, tach_reg[i]);
|
||||
+ data->volt[i] = i2c_smbus_read_byte_data(client, volt_reg[i]);
|
||||
+ data->target[i] = i2c_smbus_read_byte_data(client, target_reg[i]);
|
||||
+ data->dac[i] = i2c_smbus_read_byte_data(client, dac_reg[i]);
|
||||
+
|
||||
+
|
||||
+
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+
|
||||
+
|
||||
+static struct max6620_data *max6620_update_device(struct device *dev)
|
||||
+{
|
||||
+ int i;
|
||||
+ struct i2c_client *client = to_i2c_client(dev);
|
||||
+ struct max6620_data *data = i2c_get_clientdata(client);
|
||||
+
|
||||
+ mutex_lock(&data->update_lock);
|
||||
+
|
||||
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
|
||||
+
|
||||
+ for (i = 0; i < 4; i++) {
|
||||
+ data->fancfg[i] = i2c_smbus_read_byte_data(client, config_reg[i]);
|
||||
+ data->fandyn[i] = i2c_smbus_read_byte_data(client, dyn_reg[i]);
|
||||
+ data->tach[i] = i2c_smbus_read_byte_data(client, tach_reg[i]);
|
||||
+ data->volt[i] = i2c_smbus_read_byte_data(client, volt_reg[i]);
|
||||
+ data->target[i] = i2c_smbus_read_byte_data(client, target_reg[i]);
|
||||
+ data->dac[i] = i2c_smbus_read_byte_data(client, dac_reg[i]);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ /*
|
||||
+ * Alarms are cleared on read in case the condition that
|
||||
+ * caused the alarm is removed. Keep the value latched here
|
||||
+ * for providing the register through different alarm files.
|
||||
+ */
|
||||
+ u8 fault_reg;
|
||||
+ fault_reg = i2c_smbus_read_byte_data(client, MAX6620_REG_FAULT);
|
||||
+ data->fault |= (fault_reg >> 4) & (fault_reg & 0x0F);
|
||||
+
|
||||
+ data->last_updated = jiffies;
|
||||
+ data->valid = 1;
|
||||
+ }
|
||||
+
|
||||
+ mutex_unlock(&data->update_lock);
|
||||
+
|
||||
+ return data;
|
||||
+}
|
||||
+
|
||||
+module_i2c_driver(max6620_driver);
|
||||
+
|
||||
+static int __init max6620_init(void)
|
||||
+{
|
||||
+ return i2c_add_driver(&max6620_driver);
|
||||
+}
|
||||
+module_init(max6620_init);
|
||||
+
|
||||
+/**
|
||||
+ * sht21_init() - clean up driver
|
||||
+ *
|
||||
+ * Called when module is removed.
|
||||
+ */
|
||||
+static void __exit max6620_exit(void)
|
||||
+{
|
||||
+ i2c_del_driver(&max6620_driver);
|
||||
+}
|
||||
+module_exit(max6620_exit);
|
||||
+
|
||||
+MODULE_AUTHOR("Lucas Grunenberg");
|
||||
+MODULE_DESCRIPTION("MAX6620 sensor driver");
|
||||
+MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,78 @@
|
||||
enable PMBUS_SKIP_STATUS_CHECK for dps460
|
||||
|
||||
From: Vadim Pasternak <vadimp@mellanox.com>
|
||||
|
||||
Patch for pmbus - includes disabling of PMBus status check through platform data structure.
|
||||
This is due to some PMBus don't support the STATUS_CML register, or report communication errors
|
||||
for no explicable reason. For such chips, checking the status register must be disabled.
|
||||
---
|
||||
drivers/hwmon/pmbus/pmbus.c | 14 ++++++++++++++
|
||||
drivers/hwmon/pmbus/pmbus_core.c | 3 +++
|
||||
2 files changed, 17 insertions(+)
|
||||
|
||||
diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c
|
||||
index 7e91700..6dd75fb 100644
|
||||
--- a/drivers/hwmon/pmbus/pmbus.c
|
||||
+++ b/drivers/hwmon/pmbus/pmbus.c
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/i2c.h>
|
||||
+#include <linux/i2c/pmbus.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
/*
|
||||
@@ -166,14 +167,26 @@ static int pmbus_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct pmbus_driver_info *info;
|
||||
+ struct pmbus_platform_data *pdata = NULL;
|
||||
+ struct device *dev = &client->dev;
|
||||
|
||||
info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info),
|
||||
GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
+ if (!strncmp(id->name, "dps460", sizeof("dps460"))) {
|
||||
+ pdata = kzalloc(sizeof(struct pmbus_platform_data), GFP_KERNEL);
|
||||
+ if (!pdata) {
|
||||
+ kfree(info);
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+ pdata->flags = PMBUS_SKIP_STATUS_CHECK;
|
||||
+ }
|
||||
+
|
||||
info->pages = id->driver_data;
|
||||
info->identify = pmbus_identify;
|
||||
+ dev->platform_data = pdata;
|
||||
|
||||
return pmbus_do_probe(client, id, info);
|
||||
}
|
||||
@@ -195,6 +208,7 @@ static const struct i2c_device_id pmbus_id[] = {
|
||||
{"tps40400", 1},
|
||||
{"tps40422", 2},
|
||||
{"udt020", 1},
|
||||
+ {"dps460", 1},
|
||||
{}
|
||||
};
|
||||
|
||||
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
|
||||
index 291d11f..09b123f 100644
|
||||
--- a/drivers/hwmon/pmbus/pmbus_core.c
|
||||
+++ b/drivers/hwmon/pmbus/pmbus_core.c
|
||||
@@ -1792,8 +1792,11 @@ EXPORT_SYMBOL_GPL(pmbus_do_probe);
|
||||
int pmbus_do_remove(struct i2c_client *client)
|
||||
{
|
||||
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
+ const struct pmbus_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
kfree(data->group.attrs);
|
||||
+ if (pdata)
|
||||
+ kfree(pdata);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_do_remove);
|
||||
--
|
||||
2.1.4
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
Update pmbus_data data structure to meet kernel implementation
|
||||
|
||||
From: Shuotian Cheng <shuche@microsoft.com>
|
||||
|
||||
The pmbus_data data structure is pasted in the driver.
|
||||
Cumulus patch is for kernel 3.2.x.
|
||||
Update this data structure to meet current kernel (3.16.x) implementation.
|
||||
---
|
||||
drivers/hwmon/pmbus/dni_dps460.c | 42 +++++++++++++++-----------------------
|
||||
1 file changed, 17 insertions(+), 25 deletions(-)
|
||||
|
||||
diff --git a/drivers/hwmon/pmbus/dni_dps460.c b/drivers/hwmon/pmbus/dni_dps460.c
|
||||
index c687217..1607b65 100644
|
||||
--- a/drivers/hwmon/pmbus/dni_dps460.c
|
||||
+++ b/drivers/hwmon/pmbus/dni_dps460.c
|
||||
@@ -39,41 +39,32 @@ enum chips { dni_dps460 };
|
||||
#define FAN_VALUE_MAX 0x64
|
||||
|
||||
/* Needed to access the mutex. Copied from pmbus_core.c */
|
||||
-#define PB_NUM_STATUS_REG (PMBUS_PAGES * 6 + 1)
|
||||
+#define PB_STATUS_BASE 0
|
||||
+#define PB_STATUS_VOUT_BASE (PB_STATUS_BASE + PMBUS_PAGES)
|
||||
+#define PB_STATUS_IOUT_BASE (PB_STATUS_VOUT_BASE + PMBUS_PAGES)
|
||||
+#define PB_STATUS_FAN_BASE (PB_STATUS_IOUT_BASE + PMBUS_PAGES)
|
||||
+#define PB_STATUS_FAN34_BASE (PB_STATUS_FAN_BASE + PMBUS_PAGES)
|
||||
+#define PB_STATUS_TEMP_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES)
|
||||
+#define PB_STATUS_INPUT_BASE (PB_STATUS_TEMP_BASE + PMBUS_PAGES)
|
||||
+#define PB_STATUS_VMON_BASE (PB_STATUS_INPUT_BASE + 1)
|
||||
+#define PB_NUM_STATUS_REG (PB_STATUS_VMON_BASE + 1)
|
||||
struct pmbus_data {
|
||||
+ struct device *dev;
|
||||
struct device *hwmon_dev;
|
||||
|
||||
u32 flags; /* from platform data */
|
||||
|
||||
- int exponent; /* linear mode: exponent for output voltages */
|
||||
+ int exponent[PMBUS_PAGES];
|
||||
+ /* linear mode: exponent for output voltages */
|
||||
|
||||
const struct pmbus_driver_info *info;
|
||||
|
||||
int max_attributes;
|
||||
int num_attributes;
|
||||
- struct attribute **attributes;
|
||||
struct attribute_group group;
|
||||
+ const struct attribute_group *groups[2];
|
||||
|
||||
- /*
|
||||
- * Sensors cover both sensor and limit registers.
|
||||
- */
|
||||
- int max_sensors;
|
||||
- int num_sensors;
|
||||
struct pmbus_sensor *sensors;
|
||||
- /*
|
||||
- * Booleans are used for alarms.
|
||||
- * Values are determined from status registers.
|
||||
- */
|
||||
- int max_booleans;
|
||||
- int num_booleans;
|
||||
- struct pmbus_boolean *booleans;
|
||||
- /*
|
||||
- * Labels are used to map generic names (e.g., "in1")
|
||||
- * to PMBus specific names (e.g., "vin" or "vout1").
|
||||
- */
|
||||
- int max_labels;
|
||||
- int num_labels;
|
||||
- struct pmbus_label *labels;
|
||||
|
||||
struct mutex update_lock;
|
||||
bool valid;
|
||||
@@ -84,6 +75,7 @@ struct pmbus_data {
|
||||
* so we keep them all together.
|
||||
*/
|
||||
u8 status[PB_NUM_STATUS_REG];
|
||||
+ u8 status_register;
|
||||
|
||||
u8 currpage;
|
||||
};
|
||||
@@ -123,14 +115,14 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
int err;
|
||||
- unsigned int val;
|
||||
- unsigned int rpm;
|
||||
+ unsigned long val;
|
||||
+ unsigned long rpm;
|
||||
|
||||
err = kstrtol(buf, 10, &rpm);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
- rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
|
||||
+ rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
Add PMBUS driver for DNI DPS460 Power Supply
|
||||
|
||||
From: Cumulus Networks <support@cumulusnetworks.com>
|
||||
|
||||
|
||||
---
|
||||
drivers/hwmon/pmbus/Kconfig | 10 ++
|
||||
drivers/hwmon/pmbus/Makefile | 1
|
||||
drivers/hwmon/pmbus/dni_dps460.c | 253 ++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 264 insertions(+)
|
||||
create mode 100644 drivers/hwmon/pmbus/dni_dps460.c
|
||||
|
||||
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
|
||||
index ec48945..7d3b1aa 100644
|
||||
--- a/drivers/hwmon/pmbus/Kconfig
|
||||
+++ b/drivers/hwmon/pmbus/Kconfig
|
||||
@@ -77,6 +77,16 @@ config SENSORS_MAX34440
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called max34440.
|
||||
|
||||
+config SENSORS_DNI_DPS460
|
||||
+ tristate "Delta DPS460"
|
||||
+ default n
|
||||
+ help
|
||||
+ If you say yes here you get hardware monitoring support for Delta
|
||||
+ DPS460.
|
||||
+
|
||||
+ This driver can also be built as a module. If so, the module will
|
||||
+ be called dni_dps460.
|
||||
+
|
||||
config SENSORS_MAX8688
|
||||
tristate "Maxim MAX8688"
|
||||
default n
|
||||
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
|
||||
index 5e6c316..767d086 100644
|
||||
--- a/drivers/hwmon/pmbus/Makefile
|
||||
+++ b/drivers/hwmon/pmbus/Makefile
|
||||
@@ -9,6 +9,7 @@ obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
|
||||
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
|
||||
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
|
||||
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
|
||||
+obj-$(CONFIG_SENSORS_DNI_DPS460) += dni_dps460.o
|
||||
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
|
||||
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
|
||||
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
|
||||
diff --git a/drivers/hwmon/pmbus/dni_dps460.c b/drivers/hwmon/pmbus/dni_dps460.c
|
||||
new file mode 100644
|
||||
index 0000000..c687217
|
||||
--- /dev/null
|
||||
+++ b/drivers/hwmon/pmbus/dni_dps460.c
|
||||
@@ -0,0 +1,253 @@
|
||||
+/*
|
||||
+ * Hardware monitoring driver for Delta DPS460
|
||||
+ *
|
||||
+ * Copyright (C) 2014 Cumulus Networks, LLC
|
||||
+ * Author: Puneet Shenoy <puneet@cumulusnetworks.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.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <linux/slab.h>
|
||||
+#include <linux/i2c.h>
|
||||
+#include <linux/i2c/pmbus.h>
|
||||
+#include <linux/hwmon.h>
|
||||
+#include <linux/hwmon-sysfs.h>
|
||||
+#include "pmbus.h"
|
||||
+
|
||||
+enum chips { dni_dps460 };
|
||||
+
|
||||
+/* Data provided by DELL Inc */
|
||||
+#define FAN_RPM_MIN 7200
|
||||
+#define FAN_RPM_MAX 18000
|
||||
+#define FAN_VALUE_MIN 0x28
|
||||
+#define FAN_VALUE_MAX 0x64
|
||||
+
|
||||
+/* Needed to access the mutex. Copied from pmbus_core.c */
|
||||
+#define PB_NUM_STATUS_REG (PMBUS_PAGES * 6 + 1)
|
||||
+struct pmbus_data {
|
||||
+ struct device *hwmon_dev;
|
||||
+
|
||||
+ u32 flags; /* from platform data */
|
||||
+
|
||||
+ int exponent; /* linear mode: exponent for output voltages */
|
||||
+
|
||||
+ const struct pmbus_driver_info *info;
|
||||
+
|
||||
+ int max_attributes;
|
||||
+ int num_attributes;
|
||||
+ struct attribute **attributes;
|
||||
+ struct attribute_group group;
|
||||
+
|
||||
+ /*
|
||||
+ * Sensors cover both sensor and limit registers.
|
||||
+ */
|
||||
+ int max_sensors;
|
||||
+ int num_sensors;
|
||||
+ struct pmbus_sensor *sensors;
|
||||
+ /*
|
||||
+ * Booleans are used for alarms.
|
||||
+ * Values are determined from status registers.
|
||||
+ */
|
||||
+ int max_booleans;
|
||||
+ int num_booleans;
|
||||
+ struct pmbus_boolean *booleans;
|
||||
+ /*
|
||||
+ * Labels are used to map generic names (e.g., "in1")
|
||||
+ * to PMBus specific names (e.g., "vin" or "vout1").
|
||||
+ */
|
||||
+ int max_labels;
|
||||
+ int num_labels;
|
||||
+ struct pmbus_label *labels;
|
||||
+
|
||||
+ struct mutex update_lock;
|
||||
+ bool valid;
|
||||
+ unsigned long last_updated; /* in jiffies */
|
||||
+
|
||||
+ /*
|
||||
+ * A single status register covers multiple attributes,
|
||||
+ * so we keep them all together.
|
||||
+ */
|
||||
+ u8 status[PB_NUM_STATUS_REG];
|
||||
+
|
||||
+ u8 currpage;
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * We are only concerned with the first fan. The get_target and set_target are
|
||||
+ * are written accordingly.
|
||||
+ */
|
||||
+static ssize_t get_target(struct device *dev, struct device_attribute *devattr,
|
||||
+ char *buf) {
|
||||
+
|
||||
+ struct i2c_client *client = to_i2c_client(dev);
|
||||
+ struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
+ int val;
|
||||
+ u32 rpm;
|
||||
+
|
||||
+ /*
|
||||
+ * The FAN_COMMAND_n takes a value which is not the RPM.
|
||||
+ * The value and RPM have a liner relation.
|
||||
+ * rpm = (FAN_RPM_MIN/FAN_VALUE_MIN) * val
|
||||
+ * The slope is (FAN_RPM_MIN/FAN_VALUE_MIN) = 180
|
||||
+ */
|
||||
+ mutex_lock(&data->update_lock);
|
||||
+ val = pmbus_read_word_data(client, 0, PMBUS_FAN_COMMAND_1);
|
||||
+ pmbus_clear_faults(client);
|
||||
+ mutex_unlock(&data->update_lock);
|
||||
+ if (val < 0) {
|
||||
+ return val;
|
||||
+ }
|
||||
+ rpm = val * (FAN_RPM_MIN/FAN_VALUE_MIN);
|
||||
+ return sprintf(buf, "%d\n", rpm);
|
||||
+}
|
||||
+
|
||||
+static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
|
||||
+ const char *buf, size_t count) {
|
||||
+
|
||||
+ struct i2c_client *client = to_i2c_client(dev);
|
||||
+ struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
+ int err;
|
||||
+ unsigned int val;
|
||||
+ unsigned int rpm;
|
||||
+
|
||||
+ err = kstrtol(buf, 10, &rpm);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
|
||||
+
|
||||
+ mutex_lock(&data->update_lock);
|
||||
+
|
||||
+ val = FAN_VALUE_MIN * rpm;
|
||||
+ val /= FAN_RPM_MIN;
|
||||
+ pmbus_write_word_data(client, 0, PMBUS_FAN_COMMAND_1, (u16)val);
|
||||
+ pmbus_clear_faults(client);
|
||||
+
|
||||
+ mutex_unlock(&data->update_lock);
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+static ssize_t show_pec(struct device *dev, struct device_attribute *dummy,
|
||||
+ char *buf)
|
||||
+{
|
||||
+ struct i2c_client *client = to_i2c_client(dev);
|
||||
+ return sprintf(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC));
|
||||
+}
|
||||
+
|
||||
+static ssize_t set_pec(struct device *dev, struct device_attribute *dummy,
|
||||
+ const char *buf, size_t count)
|
||||
+{
|
||||
+ struct i2c_client *client = to_i2c_client(dev);
|
||||
+ long val;
|
||||
+ int err;
|
||||
+
|
||||
+ err = strict_strtol(buf, 10, &val);
|
||||
+ if (err < 0)
|
||||
+ return err;
|
||||
+
|
||||
+ if (val != 0)
|
||||
+ client->flags |= I2C_CLIENT_PEC;
|
||||
+ else
|
||||
+ client->flags &= ~I2C_CLIENT_PEC;
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+
|
||||
+static SENSOR_DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec, 0);
|
||||
+static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, get_target,
|
||||
+ set_target, 0);
|
||||
+
|
||||
+static struct attribute *dni_dps460_attrs[] = {
|
||||
+ &sensor_dev_attr_fan1_target.dev_attr.attr,
|
||||
+ &sensor_dev_attr_pec.dev_attr.attr,
|
||||
+ NULL
|
||||
+};
|
||||
+static struct attribute_group dni_dps460_attr_grp = {
|
||||
+ .attrs = dni_dps460_attrs,
|
||||
+};
|
||||
+
|
||||
+static int dni_dps460_probe(struct i2c_client *client,
|
||||
+ const struct i2c_device_id *id)
|
||||
+{
|
||||
+ struct pmbus_driver_info *info;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!i2c_check_functionality(client->adapter,
|
||||
+ I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
+ I2C_FUNC_SMBUS_WORD_DATA |
|
||||
+ I2C_FUNC_SMBUS_PEC))
|
||||
+ return -ENODEV;
|
||||
+
|
||||
+ /* Needs PEC(PACKET ERROR CODE). Writes wont work without this. */
|
||||
+ client->flags = I2C_CLIENT_PEC;
|
||||
+
|
||||
+ info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL);
|
||||
+ if (!info)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ /* Use only 1 page with 1 Fan, 2 Temps. */
|
||||
+ info->pages = 1;
|
||||
+ info->func[0] = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 |
|
||||
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP;
|
||||
+
|
||||
+ ret = pmbus_do_probe(client, id, info);
|
||||
+ if (ret < 0)
|
||||
+ goto out;
|
||||
+
|
||||
+ ret = sysfs_create_group(&client->dev.kobj, &dni_dps460_attr_grp);
|
||||
+ if (ret)
|
||||
+ goto out;
|
||||
+ return 0;
|
||||
+out:
|
||||
+ kfree(info);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int dni_dps460_remove(struct i2c_client *client)
|
||||
+{
|
||||
+ struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
+
|
||||
+ sysfs_remove_group(&client->dev.kobj, &dni_dps460_attr_grp);
|
||||
+ if (data->info)
|
||||
+ kfree(data->info);
|
||||
+ pmbus_do_remove(client);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct i2c_device_id dni_dps460_id[] = {
|
||||
+ {"dni_dps460", dni_dps460},
|
||||
+ {}
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(i2c, dni_dps460_id);
|
||||
+
|
||||
+static struct i2c_driver dni_dps460_driver = {
|
||||
+ .driver = {
|
||||
+ .name = "dni_dps460",
|
||||
+ },
|
||||
+ .probe = dni_dps460_probe,
|
||||
+ .remove = dni_dps460_remove,
|
||||
+ .id_table = dni_dps460_id,
|
||||
+};
|
||||
+
|
||||
+module_i2c_driver(dni_dps460_driver);
|
||||
+
|
||||
+MODULE_AUTHOR("Puneet Shenoy");
|
||||
+MODULE_DESCRIPTION("PMBus driver for Delta DPS460");
|
||||
+MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,89 @@
|
||||
mlnx patch for UCD9200
|
||||
|
||||
From: Vadim Pasternak <vadimp@mellanox.com>
|
||||
|
||||
Patch replaces in device probing routine (ucd9000_probe) call
|
||||
i2c_smbus_read_block_data with i2c_smbus_read_i2c_block_data.
|
||||
|
||||
The first call executes the SMBus "block read" protocol.
|
||||
Using this function requires that the client's adapter support
|
||||
the I2C_FUNC_SMBUS_READ_BLOCK_DATA functionality. Not all adapter
|
||||
drivers support this. In particular Mellanox i2c controller doesn't
|
||||
support it. API i2c_smbus_read_i2c_block_data is supposed to be
|
||||
more generic and be supported by all i2c client adapters.
|
||||
---
|
||||
drivers/hwmon/pmbus/ucd9200.c | 26 +++++++++++++++++++++-----
|
||||
1 files changed, 21 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/drivers/hwmon/pmbus/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c
|
||||
index 033d6ac..119130c 100644
|
||||
--- a/drivers/hwmon/pmbus/ucd9200.c
|
||||
+++ b/drivers/hwmon/pmbus/ucd9200.c
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/pmbus.h>
|
||||
+#include <linux/ctype.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
#define UCD9200_PHASE_INFO 0xd2
|
||||
@@ -52,14 +53,15 @@ static int ucd9200_probe(struct i2c_client *client,
|
||||
u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
struct pmbus_driver_info *info;
|
||||
const struct i2c_device_id *mid;
|
||||
- int i, j, ret;
|
||||
+ int i, j, ret, n, len;
|
||||
+ u8* buff;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_BLOCK_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
- ret = i2c_smbus_read_block_data(client, UCD9200_DEVICE_ID,
|
||||
+ ret = i2c_smbus_read_i2c_block_data(client, UCD9200_DEVICE_ID, 8,
|
||||
block_buffer);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read device ID\n");
|
||||
@@ -68,8 +70,22 @@ static int ucd9200_probe(struct i2c_client *client,
|
||||
block_buffer[ret] = '\0';
|
||||
dev_info(&client->dev, "Device ID %s\n", block_buffer);
|
||||
|
||||
+ len = strlen(block_buffer);
|
||||
+ for (n=0; n < len; n++) {
|
||||
+ if (isalnum(block_buffer[n]))
|
||||
+ break;
|
||||
+ }
|
||||
+ if (n >= len) {
|
||||
+ dev_err(&client->dev, "Incorrect device name\n");
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+ buff = &block_buffer[n];
|
||||
+ len = strlen(buff);
|
||||
+
|
||||
for (mid = ucd9200_id; mid->name[0]; mid++) {
|
||||
- if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
|
||||
+ if (len != strlen(mid->name))
|
||||
+ continue;
|
||||
+ if (!strncasecmp(mid->name, buff, strlen(mid->name)))
|
||||
break;
|
||||
}
|
||||
if (!mid->name[0]) {
|
||||
@@ -86,7 +102,7 @@ static int ucd9200_probe(struct i2c_client *client,
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
- ret = i2c_smbus_read_block_data(client, UCD9200_PHASE_INFO,
|
||||
+ ret = i2c_smbus_read_i2c_block_data(client, UCD9200_PHASE_INFO, 4,
|
||||
block_buffer);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read phase information\n");
|
||||
@@ -100,7 +116,7 @@ static int ucd9200_probe(struct i2c_client *client,
|
||||
* the first unconfigured rail.
|
||||
*/
|
||||
info->pages = 0;
|
||||
- for (i = 0; i < ret; i++) {
|
||||
+ for (i = 1; i < ret; i++) {
|
||||
if (!block_buffer[i])
|
||||
break;
|
||||
info->pages++;
|
||||
@@ -0,0 +1,57 @@
|
||||
Add 'delay' module param to the driver.
|
||||
|
||||
From: Cumulus Networks <support@cumulusnetworks.com>
|
||||
|
||||
This is needed on S6000 for safe PMBUS access.
|
||||
Without setting the 'delay', the ismt driver throws 'completion wait
|
||||
timed out' error message.
|
||||
---
|
||||
drivers/i2c/busses/i2c-ismt.c | 13 ++++++++++---
|
||||
1 file changed, 10 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c
|
||||
index d9ee43c..b2b3856 100644
|
||||
--- a/drivers/i2c/busses/i2c-ismt.c
|
||||
+++ b/drivers/i2c/busses/i2c-ismt.c
|
||||
@@ -70,6 +70,7 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/interrupt.h>
|
||||
+#include <linux/delay.h>
|
||||
|
||||
#include <asm-generic/io-64-nonatomic-lo-hi.h>
|
||||
|
||||
@@ -192,9 +193,12 @@ static const struct pci_device_id ismt_ids[] = {
|
||||
MODULE_DEVICE_TABLE(pci, ismt_ids);
|
||||
|
||||
/* Bus speed control bits for slow debuggers - refer to the docs for usage */
|
||||
-static unsigned int bus_speed;
|
||||
+static unsigned int bus_speed = 100;
|
||||
+static unsigned int delay = 1000;
|
||||
module_param(bus_speed, uint, S_IRUGO);
|
||||
-MODULE_PARM_DESC(bus_speed, "Bus Speed in kHz (0 = BIOS default)");
|
||||
+MODULE_PARM_DESC(bus_speed, "Bus Speed in kHz (1000 by default)");
|
||||
+module_param(delay, uint, S_IRUGO);
|
||||
+MODULE_PARM_DESC(delay, "Delay in microsecs before access (1000 by default)");
|
||||
|
||||
/**
|
||||
* __ismt_desc_dump() - dump the contents of a specific descriptor
|
||||
@@ -391,6 +395,9 @@ static int ismt_access(struct i2c_adapter *adap, u16 addr,
|
||||
struct ismt_priv *priv = i2c_get_adapdata(adap);
|
||||
struct device *dev = &priv->pci_dev->dev;
|
||||
|
||||
+ if (delay > 0)
|
||||
+ udelay(delay);
|
||||
+
|
||||
desc = &priv->hw[priv->head];
|
||||
|
||||
/* Initialize the DMA buffer */
|
||||
@@ -756,7 +763,7 @@ static void ismt_hw_init(struct ismt_priv *priv)
|
||||
bus_speed = 1000;
|
||||
break;
|
||||
}
|
||||
- dev_dbg(dev, "SMBus clock is running at %d kHz\n", bus_speed);
|
||||
+ dev_info(dev, "SMBus clock is running at %d kHz with delay %d us\n", bus_speed, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -0,0 +1,67 @@
|
||||
diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c
|
||||
index 051ea94..2a04baa 100644
|
||||
--- a/drivers/net/ethernet/intel/igb/e1000_82575.c
|
||||
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.c
|
||||
@@ -286,6 +286,9 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw)
|
||||
phy->ops.set_d3_lplu_state = igb_set_d3_lplu_state_82580;
|
||||
phy->ops.force_speed_duplex = igb_phy_force_speed_duplex_m88;
|
||||
break;
|
||||
+ case BCM54616_E_PHY_ID:
|
||||
+ phy->type = e1000_phy_bcm54616;
|
||||
+ break;
|
||||
default:
|
||||
ret_val = -E1000_ERR_PHY;
|
||||
goto out;
|
||||
@@ -1550,6 +1553,7 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
|
||||
case e1000_i350:
|
||||
case e1000_i210:
|
||||
case e1000_i211:
|
||||
+ case e1000_i354:
|
||||
phpm_reg = rd32(E1000_82580_PHY_POWER_MGMT);
|
||||
phpm_reg &= ~E1000_82580_PM_GO_LINKD;
|
||||
wr32(E1000_82580_PHY_POWER_MGMT, phpm_reg);
|
||||
@@ -1593,6 +1597,8 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw)
|
||||
case e1000_phy_82580:
|
||||
ret_val = igb_copper_link_setup_82580(hw);
|
||||
break;
|
||||
+ case e1000_phy_bcm54616:
|
||||
+ break;
|
||||
default:
|
||||
ret_val = -E1000_ERR_PHY;
|
||||
break;
|
||||
diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h
|
||||
index 217f813..5322fbf 100644
|
||||
--- a/drivers/net/ethernet/intel/igb/e1000_defines.h
|
||||
+++ b/drivers/net/ethernet/intel/igb/e1000_defines.h
|
||||
@@ -860,6 +860,7 @@
|
||||
#define M88_VENDOR 0x0141
|
||||
#define I210_I_PHY_ID 0x01410C00
|
||||
#define M88E1543_E_PHY_ID 0x01410EA0
|
||||
+#define BCM54616_E_PHY_ID 0x3625D10
|
||||
|
||||
/* M88E1000 Specific Registers */
|
||||
#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */
|
||||
diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h
|
||||
index 2003b37..d82c96b 100644
|
||||
--- a/drivers/net/ethernet/intel/igb/e1000_hw.h
|
||||
+++ b/drivers/net/ethernet/intel/igb/e1000_hw.h
|
||||
@@ -128,6 +128,7 @@ enum e1000_phy_type {
|
||||
e1000_phy_ife,
|
||||
e1000_phy_82580,
|
||||
e1000_phy_i210,
|
||||
+ e1000_phy_bcm54616,
|
||||
};
|
||||
|
||||
enum e1000_bus_type {
|
||||
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
|
||||
index e0f3664..013c1f1 100644
|
||||
--- a/drivers/net/ethernet/intel/igb/igb_main.c
|
||||
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
|
||||
@@ -108,6 +108,7 @@ static const struct pci_device_id igb_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_COPPER), board_82575 },
|
||||
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82575EB_FIBER_SERDES), board_82575 },
|
||||
{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82575GB_QUAD_COPPER), board_82575 },
|
||||
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_I354_SGMII), board_82575 },
|
||||
/* required last entry */
|
||||
{0, }
|
||||
};
|
||||
@@ -0,0 +1,141 @@
|
||||
Update SFF8436 EEPROM driver
|
||||
|
||||
From: Shuotian Cheng <shuche@microsoft.com>
|
||||
|
||||
Support newer kernel and remove eeprom_class dependency
|
||||
---
|
||||
drivers/misc/eeprom/sff_8436_eeprom.c | 27 +++++++--------------------
|
||||
include/linux/i2c/sff-8436.h | 2 --
|
||||
2 files changed, 7 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/drivers/misc/eeprom/sff_8436_eeprom.c b/drivers/misc/eeprom/sff_8436_eeprom.c
|
||||
index 0b6bf31..f5627bf 100644
|
||||
--- a/drivers/misc/eeprom/sff_8436_eeprom.c
|
||||
+++ b/drivers/misc/eeprom/sff_8436_eeprom.c
|
||||
@@ -82,7 +82,6 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/sff-8436.h>
|
||||
-#include <linux/eeprom_class.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/memory.h>
|
||||
@@ -116,7 +115,6 @@ struct sff_8436_data {
|
||||
unsigned num_addresses;
|
||||
|
||||
u8 data[SFF_8436_EEPROM_SIZE];
|
||||
- struct eeprom_device *eeprom_dev;
|
||||
|
||||
struct i2c_client *client[];
|
||||
};
|
||||
@@ -421,10 +419,9 @@ static ssize_t sff_8436_eeprom_write(struct sff_8436_data *sff_8436, const char
|
||||
{
|
||||
struct i2c_client *client = sff_8436->client[0];
|
||||
struct i2c_msg msg;
|
||||
- ssize_t status;
|
||||
unsigned long timeout, write_time;
|
||||
unsigned next_page;
|
||||
- int i = 0;
|
||||
+ int status, i = 0;
|
||||
|
||||
/* write max is at most a page */
|
||||
if (count > sff_8436->write_max)
|
||||
@@ -528,7 +525,7 @@ static ssize_t sff_8436_eeprom_update_client(struct sff_8436_data *sff_8436,
|
||||
page = sff_8436_translate_offset(sff_8436, &phy_offset);
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
- "sff_8436_eeprom_update_client off %lld page:%d phy_offset:%lld, count:%d, opcode:%d\n",
|
||||
+ "sff_8436_eeprom_update_client off %lld page:%d phy_offset:%lld, count:%zu, opcode:%d\n",
|
||||
off, page, phy_offset, count, opcode);
|
||||
if (page > 0) {
|
||||
ret = sff_8436_write_page_reg(sff_8436, page);
|
||||
@@ -705,18 +702,18 @@ static ssize_t sff_8436_read_write(struct sff_8436_data *sff_8436,
|
||||
pending_len = pending_len - page_len;
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
- "sff_read off %lld len %d page_start_offset %lld page_offset %lld page_len %d pending_len %d\n",
|
||||
+ "sff_read off %lld len %zu page_start_offset %lld page_offset %lld page_len %zu pending_len %zu\n",
|
||||
off, len, page_start_offset, page_offset, page_len, pending_len);
|
||||
|
||||
/* Refresh the data from offset for specified len */
|
||||
ret = sff_8436_eeprom_update_client(sff_8436, page_offset, page_len, opcode);
|
||||
if (ret != page_len) {
|
||||
if (err_timeout) {
|
||||
- dev_dbg(&client->dev, "sff_8436_update_client for %s page %d page_offset %lld page_len %d failed %d!\n",
|
||||
+ dev_dbg(&client->dev, "sff_8436_update_client for %s page %d page_offset %lld page_len %zu failed %d!\n",
|
||||
(page ? "Upper" : "Lower"), (page ? (page-1) : page), page_offset, page_len, ret);
|
||||
goto err;
|
||||
} else {
|
||||
- dev_err(&client->dev, "sff_8436_update_client for %s page %d page_offset %lld page_len %d failed %d!\n",
|
||||
+ dev_err(&client->dev, "sff_8436_update_client for %s page %d page_offset %lld page_len %zu failed %d!\n",
|
||||
(page ? "Upper" : "Lower"), (page ? (page-1) : page), page_offset, page_len, ret);
|
||||
}
|
||||
}
|
||||
@@ -780,15 +777,13 @@ static ssize_t sff_8436_macc_write(struct memory_accessor *macc, const char *buf
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
-static int __devexit sff_8436_remove(struct i2c_client *client)
|
||||
+static int sff_8436_remove(struct i2c_client *client)
|
||||
{
|
||||
struct sff_8436_data *sff_8436;
|
||||
|
||||
sff_8436 = i2c_get_clientdata(client);
|
||||
sysfs_remove_bin_file(&client->dev.kobj, &sff_8436->bin);
|
||||
|
||||
- eeprom_device_unregister(sff_8436->eeprom_dev);
|
||||
-
|
||||
kfree(sff_8436->writebuf);
|
||||
kfree(sff_8436);
|
||||
return 0;
|
||||
@@ -821,7 +816,6 @@ static int sff_8436_eeprom_probe(struct i2c_client *client,
|
||||
|
||||
chip.setup = NULL;
|
||||
chip.context = NULL;
|
||||
- chip.eeprom_data = NULL;
|
||||
}
|
||||
|
||||
if (!is_power_of_2(chip.byte_len))
|
||||
@@ -923,13 +917,6 @@ static int sff_8436_eeprom_probe(struct i2c_client *client,
|
||||
if (err)
|
||||
goto err_struct;
|
||||
|
||||
- sff_8436->eeprom_dev = eeprom_device_register(&client->dev, chip.eeprom_data);
|
||||
- if (IS_ERR(sff_8436->eeprom_dev)) {
|
||||
- dev_err(&client->dev, "error registering eeprom device.\n");
|
||||
- err = PTR_ERR(sff_8436->eeprom_dev);
|
||||
- goto err_sysfs_cleanup;
|
||||
- }
|
||||
-
|
||||
i2c_set_clientdata(client, sff_8436);
|
||||
|
||||
dev_info(&client->dev, "%zu byte %s EEPROM, %s\n",
|
||||
@@ -968,7 +955,7 @@ static struct i2c_driver sff_8436_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = sff_8436_eeprom_probe,
|
||||
- .remove = __devexit_p(sff_8436_remove),
|
||||
+ .remove = sff_8436_remove,
|
||||
.id_table = sff8436_ids,
|
||||
};
|
||||
|
||||
diff --git a/include/linux/i2c/sff-8436.h b/include/linux/i2c/sff-8436.h
|
||||
index cd46896..4df48ad 100644
|
||||
--- a/include/linux/i2c/sff-8436.h
|
||||
+++ b/include/linux/i2c/sff-8436.h
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/memory.h>
|
||||
-#include <linux/eeprom_class.h>
|
||||
|
||||
/*
|
||||
* As seen through Linux I2C, differences between the most common types of I2C
|
||||
@@ -27,7 +26,6 @@ struct sff_8436_platform_data {
|
||||
|
||||
void (*setup)(struct memory_accessor *, void *context);
|
||||
void *context;
|
||||
- struct eeprom_platform_data *eeprom_data; /* extra data for the eeprom_class */
|
||||
};
|
||||
|
||||
#endif /* _LINUX_SFF_8436_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1,13 @@
|
||||
driver-at24-fix-odd-length-two-byte-access.patch
|
||||
driver-hwmon-max6620.patch
|
||||
driver-hwmon-max6620-fix-rpm-calc.patch
|
||||
driver-hwmon-max6620-update.patch
|
||||
driver-hwmon-pmbus-dni_dps460.patch
|
||||
driver-hwmon-pmbus-dni_dps460-update-pmbus-core.patch
|
||||
driver-i2c-bus-intel-ismt-add-delay-param.patch
|
||||
driver-support-sff-8436-eeprom.patch
|
||||
driver-support-sff-8436-eeprom-update.patch
|
||||
driver-hwmon-pmbus-add-dps460-support.patch
|
||||
driver-hwmon-pmbus-ucd9200-mlnx.patch
|
||||
driver-arista-piix4-mux-patch.patch
|
||||
3.16-fs-overlayfs.patch
|
||||
|
||||
Reference in New Issue
Block a user