#include <linux/jiffies.h>
#include <linux/platform_device.h>
#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
-#include <asm/io.h>
+#include <linux/acpi.h>
+#include <linux/io.h>
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
static struct platform_device *pdev;
#define SUPERIO_REG_ACT 0x30
#define SUPERIO_REG_BASE 0x60
#define SUPERIO_REG_DEVID 0x20
+#define SUPERIO_REG_DEVREV 0x21
/* Logical device registers */
unsigned short addr;
const char *name;
enum chips type;
- struct class_device *class_dev;
+ struct device *hwmon_dev;
struct mutex update_lock;
unsigned long last_updated; /* In jiffies */
struct smsc47m1_sio_data {
enum chips type;
+ u8 activate; /* Remember initial device state */
};
-static int smsc47m1_probe(struct platform_device *pdev);
-static int smsc47m1_remove(struct platform_device *pdev);
+static int __exit smsc47m1_remove(struct platform_device *pdev);
static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
int init);
.owner = THIS_MODULE,
.name = DRVNAME,
},
- .probe = smsc47m1_probe,
- .remove = __devexit_p(smsc47m1_remove),
+ .remove = __exit_p(smsc47m1_remove),
};
-/* nr is 0 or 1 in the callback functions below */
-
-static ssize_t get_fan(struct device *dev, char *buf, int nr)
+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 smsc47m1_data *data = smsc47m1_update_device(dev, 0);
+ int nr = attr->index;
/* This chip (stupidly) stops monitoring fan speed if PWM is
enabled and duty cycle is 0%. This is fine if the monitoring
and control concern the same fan, but troublesome if they are
return sprintf(buf, "%d\n", rpm);
}
-static ssize_t get_fan_min(struct device *dev, char *buf, int nr)
+static ssize_t get_fan_min(struct device *dev, struct device_attribute
+ *devattr, char *buf)
{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
+ int nr = attr->index;
int rpm = MIN_FROM_REG(data->fan_preload[nr],
DIV_FROM_REG(data->fan_div[nr]));
return sprintf(buf, "%d\n", rpm);
}
-static ssize_t get_fan_div(struct device *dev, char *buf, int nr)
+static ssize_t get_fan_div(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
+ return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
+}
+
+static ssize_t get_fan_alarm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
{
+ int bitnr = to_sensor_dev_attr(devattr)->index;
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
- return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]));
+ return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
}
-static ssize_t get_pwm(struct device *dev, char *buf, int nr)
+static ssize_t get_pwm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
- return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr]));
+ return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[attr->index]));
}
-static ssize_t get_pwm_en(struct device *dev, char *buf, int nr)
+static ssize_t get_pwm_en(struct device *dev, struct device_attribute
+ *devattr, char *buf)
{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
- return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[nr]));
+ return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[attr->index]));
}
-static ssize_t get_alarms(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t get_alarms(struct device *dev, struct device_attribute
+ *devattr, char *buf)
{
struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
return sprintf(buf, "%d\n", data->alarms);
}
-static ssize_t set_fan_min(struct device *dev, const char *buf,
- size_t count, int nr)
+static ssize_t set_fan_min(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct smsc47m1_data *data = dev_get_drvdata(dev);
+ int nr = attr->index;
long rpmdiv, val = simple_strtol(buf, NULL, 10);
mutex_lock(&data->update_lock);
determined in part by the fan clock divider. This follows the principle
of least surprise; the user doesn't expect the fan minimum to change just
because the divider changed. */
-static ssize_t set_fan_div(struct device *dev, const char *buf,
- size_t count, int nr)
+static ssize_t set_fan_div(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct smsc47m1_data *data = dev_get_drvdata(dev);
-
+ int nr = attr->index;
long new_div = simple_strtol(buf, NULL, 10), tmp;
u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
return count;
}
-static ssize_t set_pwm(struct device *dev, const char *buf,
- size_t count, int nr)
+static ssize_t set_pwm(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct smsc47m1_data *data = dev_get_drvdata(dev);
-
+ int nr = attr->index;
long val = simple_strtol(buf, NULL, 10);
if (val < 0 || val > 255)
return count;
}
-static ssize_t set_pwm_en(struct device *dev, const char *buf,
- size_t count, int nr)
+static ssize_t set_pwm_en(struct device *dev, struct device_attribute
+ *devattr, const char *buf, size_t count)
{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct smsc47m1_data *data = dev_get_drvdata(dev);
-
+ int nr = attr->index;
long val = simple_strtol(buf, NULL, 10);
if (val != 0 && val != 1)
}
#define fan_present(offset) \
-static ssize_t get_fan##offset (struct device *dev, struct device_attribute *attr, char *buf) \
-{ \
- return get_fan(dev, buf, offset - 1); \
-} \
-static ssize_t get_fan##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \
-{ \
- return get_fan_min(dev, buf, offset - 1); \
-} \
-static ssize_t set_fan##offset##_min (struct device *dev, struct device_attribute *attr, \
- const char *buf, size_t count) \
-{ \
- return set_fan_min(dev, buf, count, offset - 1); \
-} \
-static ssize_t get_fan##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \
-{ \
- return get_fan_div(dev, buf, offset - 1); \
-} \
-static ssize_t set_fan##offset##_div (struct device *dev, struct device_attribute *attr, \
- const char *buf, size_t count) \
-{ \
- return set_fan_div(dev, buf, count, offset - 1); \
-} \
-static ssize_t get_pwm##offset (struct device *dev, struct device_attribute *attr, char *buf) \
-{ \
- return get_pwm(dev, buf, offset - 1); \
-} \
-static ssize_t set_pwm##offset (struct device *dev, struct device_attribute *attr, \
- const char *buf, size_t count) \
-{ \
- return set_pwm(dev, buf, count, offset - 1); \
-} \
-static ssize_t get_pwm##offset##_en (struct device *dev, struct device_attribute *attr, char *buf) \
-{ \
- return get_pwm_en(dev, buf, offset - 1); \
-} \
-static ssize_t set_pwm##offset##_en (struct device *dev, struct device_attribute *attr, \
- const char *buf, size_t count) \
-{ \
- return set_pwm_en(dev, buf, count, offset - 1); \
-} \
-static DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan##offset, \
- NULL); \
-static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
- get_fan##offset##_min, set_fan##offset##_min); \
-static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
- get_fan##offset##_div, set_fan##offset##_div); \
-static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
- get_pwm##offset, set_pwm##offset); \
-static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
- get_pwm##offset##_en, set_pwm##offset##_en);
+static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan, \
+ NULL, offset - 1); \
+static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
+ get_fan_min, set_fan_min, offset - 1); \
+static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
+ get_fan_div, set_fan_div, offset - 1); \
+static SENSOR_DEVICE_ATTR(fan##offset##_alarm, S_IRUGO, get_fan_alarm, \
+ NULL, offset - 1); \
+static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
+ get_pwm, set_pwm, offset - 1); \
+static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
+ get_pwm_en, set_pwm_en, offset - 1)
fan_present(1);
fan_present(2);
setup so we create them individually. It is still convenient to define a
group to remove them all at once. */
static struct attribute *smsc47m1_attributes[] = {
- &dev_attr_fan1_input.attr,
- &dev_attr_fan1_min.attr,
- &dev_attr_fan1_div.attr,
- &dev_attr_fan2_input.attr,
- &dev_attr_fan2_min.attr,
- &dev_attr_fan2_div.attr,
- &dev_attr_fan3_input.attr,
- &dev_attr_fan3_min.attr,
- &dev_attr_fan3_div.attr,
-
- &dev_attr_pwm1.attr,
- &dev_attr_pwm1_enable.attr,
- &dev_attr_pwm2.attr,
- &dev_attr_pwm2_enable.attr,
- &dev_attr_pwm3.attr,
- &dev_attr_pwm3_enable.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_div.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
&dev_attr_alarms.attr,
&dev_attr_name.attr,
u8 val;
superio_enter();
- val = superio_inb(SUPERIO_REG_DEVID);
+ val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
/*
* SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x
* The LPC47M292 (device id 0x6B) is somewhat compatible, but it
* supports a 3rd fan, and the pin configuration registers are
* unfortunately different.
+ * The LPC47M233 has the same device id (0x6B) but is not compatible.
+ * We check the high bit of the device revision register to
+ * differentiate them.
*/
switch (val) {
case 0x51:
- printk(KERN_INFO "smsc47m1: Found SMSC LPC47B27x\n");
+ pr_info(DRVNAME ": Found SMSC LPC47B27x\n");
sio_data->type = smsc47m1;
break;
case 0x59:
- printk(KERN_INFO "smsc47m1: Found SMSC "
- "LPC47M10x/LPC47M112/LPC47M13x\n");
+ pr_info(DRVNAME ": Found SMSC LPC47M10x/LPC47M112/LPC47M13x\n");
sio_data->type = smsc47m1;
break;
case 0x5F:
- printk(KERN_INFO "smsc47m1: Found SMSC LPC47M14x\n");
+ pr_info(DRVNAME ": Found SMSC LPC47M14x\n");
sio_data->type = smsc47m1;
break;
case 0x60:
- printk(KERN_INFO "smsc47m1: Found SMSC "
- "LPC47M15x/LPC47M192/LPC47M997\n");
+ pr_info(DRVNAME ": Found SMSC LPC47M15x/LPC47M192/LPC47M997\n");
sio_data->type = smsc47m1;
break;
case 0x6B:
- printk(KERN_INFO "smsc47m1: Found SMSC LPC47M292\n");
+ if (superio_inb(SUPERIO_REG_DEVREV) & 0x80) {
+ pr_debug(DRVNAME ": "
+ "Found SMSC LPC47M233, unsupported\n");
+ superio_exit();
+ return -ENODEV;
+ }
+
+ pr_info(DRVNAME ": Found SMSC LPC47M292\n");
sio_data->type = smsc47m2;
break;
default:
superio_select();
*addr = (superio_inb(SUPERIO_REG_BASE) << 8)
| superio_inb(SUPERIO_REG_BASE + 1);
- val = superio_inb(SUPERIO_REG_ACT);
- if (*addr == 0 || (val & 0x01) == 0) {
- printk(KERN_INFO "smsc47m1: Device is disabled, will not use\n");
+ if (*addr == 0) {
+ pr_info(DRVNAME ": Device address not set, will not use\n");
superio_exit();
return -ENODEV;
}
+ /* Enable only if address is set (needed at least on the
+ * Compaq Presario S4000NX) */
+ sio_data->activate = superio_inb(SUPERIO_REG_ACT);
+ if ((sio_data->activate & 0x01) == 0) {
+ pr_info(DRVNAME ": Enabling device\n");
+ superio_outb(SUPERIO_REG_ACT, sio_data->activate | 0x01);
+ }
+
superio_exit();
return 0;
}
-static int __devinit smsc47m1_probe(struct platform_device *pdev)
+/* Restore device to its initial state */
+static void smsc47m1_restore(const struct smsc47m1_sio_data *sio_data)
+{
+ if ((sio_data->activate & 0x01) == 0) {
+ superio_enter();
+ superio_select();
+
+ pr_info(DRVNAME ": Disabling device\n");
+ superio_outb(SUPERIO_REG_ACT, sio_data->activate);
+
+ superio_exit();
+ }
+}
+
+#define CHECK 1
+#define REQUEST 2
+#define RELEASE 3
+
+/*
+ * This function can be used to:
+ * - test for resource conflicts with ACPI
+ * - request the resources
+ * - release the resources
+ * We only allocate the I/O ports we really need, to minimize the risk of
+ * conflicts with ACPI or with other drivers.
+ */
+static int smsc47m1_handle_resources(unsigned short address, enum chips type,
+ int action, struct device *dev)
+{
+ static const u8 ports_m1[] = {
+ /* register, region length */
+ 0x04, 1,
+ 0x33, 4,
+ 0x56, 7,
+ };
+
+ static const u8 ports_m2[] = {
+ /* register, region length */
+ 0x04, 1,
+ 0x09, 1,
+ 0x2c, 2,
+ 0x35, 4,
+ 0x56, 7,
+ 0x69, 4,
+ };
+
+ int i, ports_size, err;
+ const u8 *ports;
+
+ switch (type) {
+ case smsc47m1:
+ default:
+ ports = ports_m1;
+ ports_size = ARRAY_SIZE(ports_m1);
+ break;
+ case smsc47m2:
+ ports = ports_m2;
+ ports_size = ARRAY_SIZE(ports_m2);
+ break;
+ }
+
+ for (i = 0; i + 1 < ports_size; i += 2) {
+ unsigned short start = address + ports[i];
+ unsigned short len = ports[i + 1];
+
+ switch (action) {
+ case CHECK:
+ /* Only check for conflicts */
+ err = acpi_check_region(start, len, DRVNAME);
+ if (err)
+ return err;
+ break;
+ case REQUEST:
+ /* Request the resources */
+ if (!request_region(start, len, DRVNAME)) {
+ dev_err(dev, "Region 0x%hx-0x%hx already in "
+ "use!\n", start, start + len);
+
+ /* Undo all requests */
+ for (i -= 2; i >= 0; i -= 2)
+ release_region(address + ports[i],
+ ports[i + 1]);
+ return -EBUSY;
+ }
+ break;
+ case RELEASE:
+ /* Release the resources */
+ release_region(start, len);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int __init smsc47m1_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct smsc47m1_sio_data *sio_data = dev->platform_data;
struct smsc47m1_data *data;
struct resource *res;
- int err = 0;
+ int err;
int fan1, fan2, fan3, pwm1, pwm2, pwm3;
static const char *names[] = {
};
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (!request_region(res->start, SMSC_EXTENT, DRVNAME)) {
- dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
- (unsigned long)res->start,
- (unsigned long)res->end);
- return -EBUSY;
- }
+ err = smsc47m1_handle_resources(res->start, sio_data->type,
+ REQUEST, dev);
+ if (err < 0)
+ return err;
if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
err = -ENOMEM;
/* Register sysfs hooks */
if (fan1) {
- if ((err = device_create_file(dev, &dev_attr_fan1_input))
- || (err = device_create_file(dev, &dev_attr_fan1_min))
- || (err = device_create_file(dev, &dev_attr_fan1_div)))
+ if ((err = device_create_file(dev,
+ &sensor_dev_attr_fan1_input.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_fan1_min.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_fan1_div.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_fan1_alarm.dev_attr)))
goto error_remove_files;
} else
dev_dbg(dev, "Fan 1 not enabled by hardware, skipping\n");
if (fan2) {
- if ((err = device_create_file(dev, &dev_attr_fan2_input))
- || (err = device_create_file(dev, &dev_attr_fan2_min))
- || (err = device_create_file(dev, &dev_attr_fan2_div)))
+ if ((err = device_create_file(dev,
+ &sensor_dev_attr_fan2_input.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_fan2_min.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_fan2_div.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_fan2_alarm.dev_attr)))
goto error_remove_files;
} else
dev_dbg(dev, "Fan 2 not enabled by hardware, skipping\n");
if (fan3) {
- if ((err = device_create_file(dev, &dev_attr_fan3_input))
- || (err = device_create_file(dev, &dev_attr_fan3_min))
- || (err = device_create_file(dev, &dev_attr_fan3_div)))
+ if ((err = device_create_file(dev,
+ &sensor_dev_attr_fan3_input.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_fan3_min.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_fan3_div.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_fan3_alarm.dev_attr)))
goto error_remove_files;
- } else
+ } else if (data->type == smsc47m2)
dev_dbg(dev, "Fan 3 not enabled by hardware, skipping\n");
if (pwm1) {
- if ((err = device_create_file(dev, &dev_attr_pwm1))
- || (err = device_create_file(dev, &dev_attr_pwm1_enable)))
+ if ((err = device_create_file(dev,
+ &sensor_dev_attr_pwm1.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_pwm1_enable.dev_attr)))
goto error_remove_files;
} else
dev_dbg(dev, "PWM 1 not enabled by hardware, skipping\n");
if (pwm2) {
- if ((err = device_create_file(dev, &dev_attr_pwm2))
- || (err = device_create_file(dev, &dev_attr_pwm2_enable)))
+ if ((err = device_create_file(dev,
+ &sensor_dev_attr_pwm2.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_pwm2_enable.dev_attr)))
goto error_remove_files;
} else
dev_dbg(dev, "PWM 2 not enabled by hardware, skipping\n");
if (pwm3) {
- if ((err = device_create_file(dev, &dev_attr_pwm3))
- || (err = device_create_file(dev, &dev_attr_pwm3_enable)))
+ if ((err = device_create_file(dev,
+ &sensor_dev_attr_pwm3.dev_attr))
+ || (err = device_create_file(dev,
+ &sensor_dev_attr_pwm3_enable.dev_attr)))
goto error_remove_files;
- } else
+ } else if (data->type == smsc47m2)
dev_dbg(dev, "PWM 3 not enabled by hardware, skipping\n");
if ((err = device_create_file(dev, &dev_attr_alarms)))
goto error_remove_files;
+ if ((err = device_create_file(dev, &dev_attr_name)))
+ goto error_remove_files;
- data->class_dev = hwmon_device_register(dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ data->hwmon_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
goto error_remove_files;
}
error_remove_files:
sysfs_remove_group(&dev->kobj, &smsc47m1_group);
error_free:
+ platform_set_drvdata(pdev, NULL);
kfree(data);
error_release:
- release_region(res->start, SMSC_EXTENT);
+ smsc47m1_handle_resources(res->start, sio_data->type, RELEASE, dev);
return err;
}
-static int __devexit smsc47m1_remove(struct platform_device *pdev)
+static int __exit smsc47m1_remove(struct platform_device *pdev)
{
struct smsc47m1_data *data = platform_get_drvdata(pdev);
struct resource *res;
- platform_set_drvdata(pdev, NULL);
- hwmon_device_unregister(data->class_dev);
+ hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group);
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- release_region(res->start, SMSC_EXTENT);
+ smsc47m1_handle_resources(res->start, data->type, RELEASE, &pdev->dev);
+ platform_set_drvdata(pdev, NULL);
kfree(data);
return 0;
};
int err;
+ err = smsc47m1_handle_resources(address, sio_data->type, CHECK, NULL);
+ if (err)
+ goto exit;
+
pdev = platform_device_alloc(DRVNAME, address);
if (!pdev) {
err = -ENOMEM;
goto exit_device_put;
}
- pdev->dev.platform_data = kmalloc(sizeof(struct smsc47m1_sio_data),
- GFP_KERNEL);
- if (!pdev->dev.platform_data) {
- err = -ENOMEM;
+ err = platform_device_add_data(pdev, sio_data,
+ sizeof(struct smsc47m1_sio_data));
+ if (err) {
printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
goto exit_device_put;
}
- memcpy(pdev->dev.platform_data, sio_data,
- sizeof(struct smsc47m1_sio_data));
err = platform_device_add(pdev);
if (err) {
if (smsc47m1_find(&address, &sio_data))
return -ENODEV;
- err = platform_driver_register(&smsc47m1_driver);
+ /* Sets global pdev as a side effect */
+ err = smsc47m1_device_add(address, &sio_data);
if (err)
goto exit;
- /* Sets global pdev as a side effect */
- err = smsc47m1_device_add(address, &sio_data);
+ err = platform_driver_probe(&smsc47m1_driver, smsc47m1_probe);
if (err)
- goto exit_driver;
+ goto exit_device;
return 0;
-exit_driver:
- platform_driver_unregister(&smsc47m1_driver);
+exit_device:
+ platform_device_unregister(pdev);
+ smsc47m1_restore(&sio_data);
exit:
return err;
}
static void __exit sm_smsc47m1_exit(void)
{
- platform_device_unregister(pdev);
platform_driver_unregister(&smsc47m1_driver);
+ smsc47m1_restore(pdev->dev.platform_data);
+ platform_device_unregister(pdev);
}
MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");