Skip to content

Commit

Permalink
Attempt at becoming a ssam device driver.
Browse files Browse the repository at this point in the history
  • Loading branch information
iwanders committed Dec 5, 2023
1 parent 3ab20d1 commit 593ced7
Showing 1 changed file with 28 additions and 54 deletions.
82 changes: 28 additions & 54 deletions drivers/platform/surface/surface_fan.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
* Copyright (C) 2023 Ivor Wanders <ivor@iwanders.net>
*/

#include <linux/acpi.h>
#include <linux/hwmon.h>
#include <linux/kernel.h>
#include <linux/module.h>
Expand All @@ -14,9 +13,7 @@
#include <linux/thermal.h>
#include <linux/types.h>

// This is a non-SSAM client driver as per the surface documentation.
// It registers itself as an ACPI driver for PNP0C0B, which requires removing
// the default fan module.
// This is a SSAM client driver as per the surface documentation.
// It both sets up as a cooling device through the thermal cooling device as
// well as a hwmon fan for monitoring.

Expand All @@ -29,10 +26,10 @@

struct fan_data {
struct device *dev;
struct ssam_controller *ctrl;
struct acpi_device *acpi_fan;

struct thermal_cooling_device *cdev;

struct ssam_device *sdev;

struct device *hdev;
};

Expand All @@ -51,13 +48,6 @@ SSAM_DEFINE_SYNC_REQUEST_R(__ssam_fan_get, __le16, {
.instance_id = 0x01,
});

// ACPI
static const struct acpi_device_id surface_fan_match[] = {
{ "PNP0C0B" },
{},
};
MODULE_DEVICE_TABLE(acpi, surface_fan_match);


// Thermal cooling device
static int surface_fan_set_cur_state(struct thermal_cooling_device *cdev,
Expand All @@ -66,7 +56,7 @@ static int surface_fan_set_cur_state(struct thermal_cooling_device *cdev,
__le16 value;
struct fan_data *d = cdev->devdata;
value = cpu_to_le16(clamp(state, 0lu, (1lu << 16)));
return __ssam_fan_set(d->ctrl, &value);
return __ssam_fan_set(d->sdev->ctrl, &value);
}

static int surface_fan_get_cur_state(struct thermal_cooling_device *cdev,
Expand All @@ -75,7 +65,7 @@ static int surface_fan_get_cur_state(struct thermal_cooling_device *cdev,
int res;
struct fan_data *d = cdev->devdata;
__le16 value = 0;
res = __ssam_fan_get(d->ctrl, &value);
res = __ssam_fan_get(d->sdev->ctrl, &value);
*state = le16_to_cpu(value);
return res;
}
Expand Down Expand Up @@ -121,15 +111,15 @@ static int surface_fan_hwmon_read(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct fan_data *data = dev_get_drvdata(dev);
struct fan_data *d = dev_get_drvdata(dev);
__le16 value;
int res;

switch (type) {
case hwmon_fan:
switch (attr) {
case hwmon_fan_input:
res = __ssam_fan_get(data->ctrl, &value);
res = __ssam_fan_get(d->sdev->ctrl, &value);
if (res) {
return -1;
}
Expand Down Expand Up @@ -161,14 +151,14 @@ static int surface_fan_hwmon_write(struct device *dev,
u32 attr, int channel, long val)
{
__le16 value;
struct fan_data *data = dev_get_drvdata(dev);
struct fan_data *d = dev_get_drvdata(dev);

switch (type) {
case hwmon_fan:
switch (attr) {
case hwmon_fan_target:
value = cpu_to_le16(clamp(val, 0l, (1l << 16)));
return __ssam_fan_set(data->ctrl, &value);
return __ssam_fan_set(d->sdev->ctrl, &value);
default:
break;
}
Expand Down Expand Up @@ -200,29 +190,23 @@ static const struct hwmon_chip_info surface_fan_chip_info = {
.info = surface_fan_info,
};

static int surface_fan_probe(struct platform_device *pdev)
static int surface_fan_probe(struct ssam_device *sdev)
{
struct acpi_device *acpi_fan = ACPI_COMPANION(&pdev->dev);

struct ssam_controller *ctrl;
struct fan_data *data;
struct thermal_cooling_device *cdev;
struct device *hdev;
__le16 value;
int status;

ctrl = ssam_client_bind(&pdev->dev);
if (IS_ERR(ctrl))
return PTR_ERR(ctrl) == -ENODEV ? -EPROBE_DEFER : PTR_ERR(ctrl);

printk(KERN_INFO "In probe!");
// Probe the fan to confirm we actually have it by retrieving the
// speed.
status = __ssam_fan_get(ctrl, &value);
status = __ssam_fan_get(sdev->ctrl, &value);
if (status) {
return -ENODEV;
}

data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
data = devm_kzalloc(&sdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;

Expand All @@ -232,55 +216,45 @@ static int surface_fan_probe(struct platform_device *pdev)
return PTR_ERR(cdev);


hdev = devm_hwmon_device_register_with_info(&pdev->dev, "fan", data,
hdev = devm_hwmon_device_register_with_info(&sdev->dev, "fan", data,
&surface_fan_chip_info,
NULL);
if (IS_ERR(hdev)) {
return PTR_ERR(hdev);
}

data->dev = &pdev->dev;
data->ctrl = ctrl;
data->acpi_fan = acpi_fan;

data->sdev = sdev;
data->cdev = cdev;
data->hdev = hdev;

acpi_fan->driver_data = data;
platform_set_drvdata(pdev, data);
ssam_device_set_drvdata(sdev, data);

return 0;
}

static int surface_fan_remove(struct platform_device *pdev)
static void surface_fan_remove(struct ssam_device *sdev)
{
struct fan_data *d = platform_get_drvdata(pdev);
struct fan_data *d = ssam_device_get_drvdata(sdev);
thermal_cooling_device_unregister(d->cdev);
return 0;
}


static struct platform_driver surface_fan = {
static const struct ssam_device_id ssam_fan_match[] = {
{ SSAM_SDEV(FAN, SAM, SSAM_SSH_IID_ANY, SSAM_SSH_FUN_ANY) },
{ },
};
MODULE_DEVICE_TABLE(ssam, ssam_fan_match);

static struct ssam_device_driver surface_fan = {
.probe = surface_fan_probe,
.remove = surface_fan_remove,
.match_table = ssam_fan_match,
.driver = {
.name = "surface_fan",
.acpi_match_table = surface_fan_match,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};

static int __init surface_fan_init(void)
{
return platform_driver_register(&surface_fan);
}
module_init(surface_fan_init);

static void __exit surface_fan_exit(void)
{
platform_driver_unregister(&surface_fan);
}
module_exit(surface_fan_exit);
module_ssam_device_driver(surface_fan);

MODULE_AUTHOR("Ivor Wanders <ivor@iwanders.net>");
MODULE_DESCRIPTION("Fan Driver for Surface System Aggregator Module");
Expand Down

0 comments on commit 593ced7

Please sign in to comment.