2009-07-05 13:58:19

by Borislav Petkov

[permalink] [raw]
Subject: [PATCH] Acerhdf: fix fan control for BIOS 3309

Hi Len,

please apply.

---
From: Borislav Petkov <[email protected]>
Date: Sun, 5 Jul 2009 14:35:01 +0200
Subject: [PATCH] Acerhdf: fix fan control for BIOS 3309

With BIOS update v3309, the Aspire One has had some changes to how
the fan is being controlled. Reads to the fanreg (0x55) do not simply
return the on/off values of the fan anymore but rather a different
fan stage based on the current temperature. Empirically, I could
observe the fan stage being 0x1 when booting the machine, then the
temperature went up and the BIOS switched the fan to stage 0x2, making
it rotate faster with final stage being fan state 0x3, aka max.

This requires some changes to the controlling code so that it can still
be done adequately. Also, the OFF switch has changed from 0x21 to 0x20.
It seems almost as if they're using the least significant nibble of the
command written to the ioport for the different fan stages.

While at it, convert the fancmd[] array to a real struct thus
disambiguating command handling and making code more readable.

Signed-off-by: Borislav Petkov <[email protected]>
---
drivers/platform/x86/acerhdf.c | 20 +++++++++++++-------
1 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index bdfee17..b2ef9f5 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -108,13 +108,18 @@ MODULE_PARM_DESC(verbose, "Enable verbose dmesg output");
module_param_string(force_bios, force_bios, 16, 0);
MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check");

+struct fancmd {
+ u8 cmd_off;
+ u8 cmd_auto;
+};
+
/* BIOS settings */
struct bios_settings_t {
const char *vendor;
const char *version;
unsigned char fanreg;
unsigned char tempreg;
- unsigned char fancmd[2]; /* fan off and auto commands */
+ struct fancmd cmd;
};

/* Register addresses and values for different BIOS versions */
@@ -125,7 +130,7 @@ static const struct bios_settings_t bios_tbl[] = {
{"Acer", "v0.3304", 0x55, 0x58, {0xaf, 0x00} },
{"Acer", "v0.3305", 0x55, 0x58, {0xaf, 0x00} },
{"Acer", "v0.3308", 0x55, 0x58, {0x21, 0x00} },
- {"Acer", "v0.3309", 0x55, 0x58, {0x21, 0x00} },
+ {"Acer", "v0.3309", 0x55, 0x58, {0x20, 0x00} },
{"Acer", "v0.3310", 0x55, 0x58, {0x21, 0x00} },
{"Gateway", "v0.3103", 0x55, 0x58, {0x21, 0x00} },
{"Packard Bell", "v0.3105", 0x55, 0x58, {0x21, 0x00} },
@@ -134,7 +139,6 @@ static const struct bios_settings_t bios_tbl[] = {

static const struct bios_settings_t *bios_cfg __read_mostly;

-
static int acerhdf_get_temp(int *temp)
{
u8 read_temp;
@@ -150,13 +154,14 @@ static int acerhdf_get_temp(int *temp)
static int acerhdf_get_fanstate(int *state)
{
u8 fan;
- bool tmp;

if (ec_read(bios_cfg->fanreg, &fan))
return -EINVAL;

- tmp = (fan == bios_cfg->fancmd[ACERHDF_FAN_OFF]);
- *state = tmp ? ACERHDF_FAN_OFF : ACERHDF_FAN_AUTO;
+ if (fan != bios_cfg->cmd.cmd_off)
+ *state = ACERHDF_FAN_AUTO;
+ else
+ *state = ACERHDF_FAN_OFF;

return 0;
}
@@ -175,7 +180,8 @@ static void acerhdf_change_fanstate(int state)
state = ACERHDF_FAN_AUTO;
}

- cmd = bios_cfg->fancmd[state];
+ cmd = (state == ACERHDF_FAN_OFF) ? bios_cfg->cmd.cmd_off
+ : bios_cfg->cmd.cmd_auto;
fanstate = state;

ec_write(bios_cfg->fanreg, cmd);
--
1.6.3.1

--
Regards/Gruss,
Boris.


2009-07-05 16:51:19

by Peter Kästle

[permalink] [raw]
Subject: Re: [PATCH] Acerhdf: fix fan control for BIOS 3309

Hi Boris,

Borislav Petkov writes:

> Hi Len,
>
> please apply.
>
> ---
> From: Borislav Petkov <[email protected]>
> Date: Sun, 5 Jul 2009 14:35:01 +0200
> Subject: [PATCH] Acerhdf: fix fan control for BIOS 3309
>
> With BIOS update v3309, the Aspire One has had some changes to how
> the fan is being controlled. Reads to the fanreg (0x55) do not simply
> return the on/off values of the fan anymore but rather a different
> fan stage based on the current temperature. Empirically, I could
> observe the fan stage being 0x1 when booting the machine, then the
> temperature went up and the BIOS switched the fan to stage 0x2, making
> it rotate faster with final stage being fan state 0x3, aka max.

I think it was already like that before v3309. But in my opinion it doesn't
matter how fast the fan spins. As soon as it's spinning it's making too much
noise. That's why I like to use only two states for the fan, "on" (noisy,
doesn't matter how fast it's spinning) and "off" (quiet ;))

> This requires some changes to the controlling code so that it can still
> be done adequately. Also, the OFF switch has changed from 0x21 to 0x20.
> It seems almost as if they're using the least significant nibble of the
> command written to the ioport for the different fan stages.

I've been developping acerhdf with BIOS v3309 since the very beginning. And
it was working fine with 0x21 all the time. I'm going to do some further
inverstigations next few days. Thanks for reporting!

>
> While at it, convert the fancmd[] array to a real struct thus
> disambiguating command handling and making code more readable.

Good!

kind regards,
--peter

2009-07-05 19:08:22

by Andreas Mohr

[permalink] [raw]
Subject: Re: [PATCH] Acerhdf: fix fan control for BIOS 3309

Hi,

On Sun, Jul 05, 2009 at 06:51:13PM +0200, Peter Feuerer wrote:
> Hi Boris,
>
> Borislav Petkov writes:
>> With BIOS update v3309, the Aspire One has had some changes to how
>> the fan is being controlled. Reads to the fanreg (0x55) do not simply
>> return the on/off values of the fan anymore but rather a different
>> fan stage based on the current temperature. Empirically, I could
>> observe the fan stage being 0x1 when booting the machine, then the
>> temperature went up and the BIOS switched the fan to stage 0x2, making
>> it rotate faster with final stage being fan state 0x3, aka max.
>
> I think it was already like that before v3309. But in my opinion it
> doesn't matter how fast the fan spins. As soon as it's spinning it's
> making too much noise. That's why I like to use only two states for the
> fan, "on" (noisy, doesn't matter how fast it's spinning) and "off" (quiet
> ;))

I really don't think so.

If you're in a meeting (think university environment, where _many_ netbooks
end up being used due to highly mobile requirements...)
or a moderately occupied room, then there's some ambient noise
(say, one guy speaking) which I'm pretty damn sure fully drowns out
the first level of fan operation but certainly NOT the second or third level
(extrapolating from my very own experience here...).

Due to these effects I'd think we want to adapt the driver to handle this.
Though I'm very well aware that going the extra mile of "intelligent"
temperature handling might cause a ton of extra risks if we aren't
careful. ;)

Still, I believe it would be very useful to keep fan operation low for
moderate distances beyond fan trip point.

And ideally add a module parameter for aggressive ("binary" fan
operation) vs. soft ("intelligent" fan operation) handling, whatever
a user desires, for noise or power consumption or whatever reasons.

Andreas Mohr

2009-07-06 07:44:02

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH] Acerhdf: fix fan control for BIOS 3309

Hi Peter,

On Sun, Jul 05, 2009 at 06:51:13PM +0200, Peter Feuerer wrote:
> >With BIOS update v3309, the Aspire One has had some changes to how
> >the fan is being controlled. Reads to the fanreg (0x55) do not simply
> >return the on/off values of the fan anymore but rather a different
> >fan stage based on the current temperature. Empirically, I could
> >observe the fan stage being 0x1 when booting the machine, then the
> >temperature went up and the BIOS switched the fan to stage 0x2, making
> >it rotate faster with final stage being fan state 0x3, aka max.
>
> I think it was already like that before v3309. But in my opinion it
> doesn't matter how fast the fan spins. As soon as it's spinning it's
> making too much noise. That's why I like to use only two states for
> the fan, "on" (noisy, doesn't matter how fast it's spinning) and
> "off" (quiet ;))

Agreed. I'd like to keep that in the patch description though so that we
have that behavior documented somewhere.

> >This requires some changes to the controlling code so that it can
> >still be done adequately. Also, the OFF switch has changed from 0x21
> >to 0x20. It seems almost as if they're using the least significant
> >nibble of the command written to the ioport for the different fan
> >stages.
>
> I've been developping acerhdf with BIOS v3309 since the very
> beginning. And it was working fine with 0x21 all the time. I'm
> going to do some further inverstigations next few days. Thanks for
> reporting!

The thing I'm observing here with the 0x21 command is that whenever it
is written into the ->fanreg, the fan gets turned on for maybe a second
or less and then off again. The 0x20 keeps it off just fine. What does
your BIOS version string say, here it is:

[33340.535861] acerhdf: Acer Aspire One Fan driver, v.0.5.14
[33340.537078] acerhdf: BIOS info: Acer v0.3309, product: AOA150

--
Regards/Gruss,
Boris.

2009-07-06 07:49:18

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH] Acerhdf: fix fan control for BIOS 3309

On Sun, Jul 05, 2009 at 09:08:15PM +0200, Andreas Mohr wrote:
> Hi,
>
> On Sun, Jul 05, 2009 at 06:51:13PM +0200, Peter Feuerer wrote:
> > Hi Boris,
> >
> > Borislav Petkov writes:
> >> With BIOS update v3309, the Aspire One has had some changes to how
> >> the fan is being controlled. Reads to the fanreg (0x55) do not simply
> >> return the on/off values of the fan anymore but rather a different
> >> fan stage based on the current temperature. Empirically, I could
> >> observe the fan stage being 0x1 when booting the machine, then the
> >> temperature went up and the BIOS switched the fan to stage 0x2, making
> >> it rotate faster with final stage being fan state 0x3, aka max.
> >
> > I think it was already like that before v3309. But in my opinion it
> > doesn't matter how fast the fan spins. As soon as it's spinning it's
> > making too much noise. That's why I like to use only two states for the
> > fan, "on" (noisy, doesn't matter how fast it's spinning) and "off" (quiet
> > ;))
>
> I really don't think so.
>
> If you're in a meeting (think university environment, where _many_ netbooks
> end up being used due to highly mobile requirements...)
> or a moderately occupied room, then there's some ambient noise
> (say, one guy speaking) which I'm pretty damn sure fully drowns out
> the first level of fan operation but certainly NOT the second or third level
> (extrapolating from my very own experience here...).
>
> Due to these effects I'd think we want to adapt the driver to handle this.
> Though I'm very well aware that going the extra mile of "intelligent"
> temperature handling might cause a ton of extra risks if we aren't
> careful. ;)
>
> Still, I believe it would be very useful to keep fan operation low for
> moderate distances beyond fan trip point.
>
> And ideally add a module parameter for aggressive ("binary" fan
> operation) vs. soft ("intelligent" fan operation) handling, whatever
> a user desires, for noise or power consumption or whatever reasons.

That functionality is already in the BIOS. At least the v3309 I got
here. You simply remove the module and it does the intelligent control
with the different fan levels based on current temperature. But when
you're somewhere really quiet or you're getting annoyed even by the
level 1 fan rotation you'd still like to kill that noise (you know how
those cheap fans are :)) and that's when the module comes in.

--
Regards/Gruss,
Boris.

2009-07-07 20:08:12

by Peter Kästle

[permalink] [raw]
Subject: Re: [PATCH] Acerhdf: fix fan control for BIOS 3309

Hi Boris,

On Mon, 6 Jul 2009 09:43:42 +0200
Borislav Petkov <[email protected]> wrote:

> The thing I'm observing here with the 0x21 command is that whenever it
> is written into the ->fanreg, the fan gets turned on for maybe a second
> or less and then off again. The 0x20 keeps it off just fine. What does
> your BIOS version string say, here it is:
>
> [33340.535861] acerhdf: Acer Aspire One Fan driver, v.0.5.14
> [33340.537078] acerhdf: BIOS info: Acer v0.3309, product: AOA150

I tested your patch with value 0x20 on my machine ("AOA110" product, "v0.3309" version).
The fan was freaking out and spinning high like a helicopter.

I think we have to distinguish between the BIOS products too. Here a patch, please have a look at it and test it.

cheers,
peter

Peter:
o Applied Borislav Petkov's patch
o Added BIOS product to BIOS table as AOA110 and AOA150 have
different register values
o Added force_product parameter to allow forcing different
product
o Fixed linker warning caused by "acerhdf_drv" not being named
"acerhdf_driver"
o Added Packard Bell DOA150 BIOS settings

Signed-off-by: Peter Feuerer <[email protected]>
---
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index bdfee17..29ded3d 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -52,7 +52,7 @@
*/
#undef START_IN_KERNEL_MODE

-#define DRV_VER "0.5.13"
+#define DRV_VER "0.5.15"

/*
* According to the Atom N270 datasheet,
@@ -90,6 +90,7 @@ static unsigned int fanoff = 58;
static unsigned int verbose;
static unsigned int fanstate = ACERHDF_FAN_AUTO;
static char force_bios[16];
+static char force_product[16];
static unsigned int prev_interval;
struct thermal_zone_device *thz_dev;
struct thermal_cooling_device *cl_dev;
@@ -107,34 +108,54 @@ module_param(verbose, uint, 0600);
MODULE_PARM_DESC(verbose, "Enable verbose dmesg output");
module_param_string(force_bios, force_bios, 16, 0);
MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check");
+module_param_string(force_product, force_product, 16, 0);
+MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check");
+
+/*
+ * cmd_off: to switch the fan completely off / to check if the fan is off
+ * cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then
+ * the fan speed depending on the temperature
+ */
+struct fancmd {
+ u8 cmd_off;
+ u8 cmd_auto;
+};

/* BIOS settings */
struct bios_settings_t {
const char *vendor;
+ const char *product;
const char *version;
unsigned char fanreg;
unsigned char tempreg;
- unsigned char fancmd[2]; /* fan off and auto commands */
+ struct fancmd cmd;
};

/* Register addresses and values for different BIOS versions */
static const struct bios_settings_t bios_tbl[] = {
- {"Acer", "v0.3109", 0x55, 0x58, {0x1f, 0x00} },
- {"Acer", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
- {"Acer", "v0.3301", 0x55, 0x58, {0xaf, 0x00} },
- {"Acer", "v0.3304", 0x55, 0x58, {0xaf, 0x00} },
- {"Acer", "v0.3305", 0x55, 0x58, {0xaf, 0x00} },
- {"Acer", "v0.3308", 0x55, 0x58, {0x21, 0x00} },
- {"Acer", "v0.3309", 0x55, 0x58, {0x21, 0x00} },
- {"Acer", "v0.3310", 0x55, 0x58, {0x21, 0x00} },
- {"Gateway", "v0.3103", 0x55, 0x58, {0x21, 0x00} },
- {"Packard Bell", "v0.3105", 0x55, 0x58, {0x21, 0x00} },
- {"", "", 0, 0, {0, 0} }
+ /* working for AOA110 and AOA150 and other */
+ {"Acer", "AO", "v0.3109", 0x55, 0x58, {0x1f, 0x00} },
+ {"Acer", "AO", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
+ {"Acer", "AO", "v0.3301", 0x55, 0x58, {0xaf, 0x00} },
+ {"Acer", "AO", "v0.3304", 0x55, 0x58, {0xaf, 0x00} },
+ {"Acer", "AO", "v0.3305", 0x55, 0x58, {0xaf, 0x00} },
+ {"Acer", "AO", "v0.3308", 0x55, 0x58, {0x21, 0x00} },
+ /* AOA110 */
+ {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00} },
+ {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00} },
+ /* AOA150 */
+ {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00} },
+ {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00} },
+ /* special BIOS / other */
+ {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x00} },
+ {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00} },
+ {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x00} },
+ /* pewpew-terminator */
+ {"", "", "", 0, 0, {0, 0} }
};

static const struct bios_settings_t *bios_cfg __read_mostly;

-
static int acerhdf_get_temp(int *temp)
{
u8 read_temp;
@@ -150,13 +171,14 @@ static int acerhdf_get_temp(int *temp)
static int acerhdf_get_fanstate(int *state)
{
u8 fan;
- bool tmp;

if (ec_read(bios_cfg->fanreg, &fan))
return -EINVAL;

- tmp = (fan == bios_cfg->fancmd[ACERHDF_FAN_OFF]);
- *state = tmp ? ACERHDF_FAN_OFF : ACERHDF_FAN_AUTO;
+ if (fan != bios_cfg->cmd.cmd_off)
+ *state = ACERHDF_FAN_AUTO;
+ else
+ *state = ACERHDF_FAN_OFF;

return 0;
}
@@ -175,7 +197,8 @@ static void acerhdf_change_fanstate(int state)
state = ACERHDF_FAN_AUTO;
}

- cmd = bios_cfg->fancmd[state];
+ cmd = (state == ACERHDF_FAN_OFF) ? bios_cfg->cmd.cmd_off
+ : bios_cfg->cmd.cmd_auto;
fanstate = state;

ec_write(bios_cfg->fanreg, cmd);
@@ -437,7 +460,7 @@ static int acerhdf_remove(struct platform_device *device)
return 0;
}

-struct platform_driver acerhdf_drv = {
+static struct platform_driver acerhdf_driver = {
.driver = {
.name = "acerhdf",
.owner = THIS_MODULE,
@@ -454,24 +477,29 @@ static int acerhdf_check_hardware(void)
{
char const *vendor, *version, *product;
int i;
+ unsigned long prod_len = 0;

/* get BIOS data */
vendor = dmi_get_system_info(DMI_SYS_VENDOR);
version = dmi_get_system_info(DMI_BIOS_VERSION);
product = dmi_get_system_info(DMI_PRODUCT_NAME);

+
pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER);

- if (!force_bios[0]) {
- if (strncmp(product, "AO", 2)) {
- pr_err("no Aspire One hardware found\n");
- return -EINVAL;
- }
- } else {
- pr_info("forcing BIOS version: %s\n", version);
+ if (force_bios[0]) {
version = force_bios;
+ pr_info("forcing BIOS version: %s\n", version);
+ kernelmode = 0;
+ }
+
+ if (force_product[0]) {
+ product = force_product;
+ pr_info("forcing BIOS product: %s\n", product);
kernelmode = 0;
}
+
+ prod_len = strlen(product);

if (verbose)
pr_info("BIOS info: %s %s, product: %s\n",
@@ -479,7 +507,9 @@ static int acerhdf_check_hardware(void)

/* search BIOS version and vendor in BIOS settings table */
for (i = 0; bios_tbl[i].version[0]; i++) {
- if (!strcmp(bios_tbl[i].vendor, vendor) &&
+ if (strlen(bios_tbl[i].product) >= prod_len &&
+ !strncmp(bios_tbl[i].product, product, prod_len) &&
+ !strcmp(bios_tbl[i].vendor, vendor) &&
!strcmp(bios_tbl[i].version, version)) {
bios_cfg = &bios_tbl[i];
break;
@@ -487,8 +517,8 @@ static int acerhdf_check_hardware(void)
}

if (!bios_cfg) {
- pr_err("unknown (unsupported) BIOS version %s/%s, "
- "please report, aborting!\n", vendor, version);
+ pr_err("unknown (unsupported) BIOS version %s/%s/%s, "
+ "please report, aborting!\n", vendor, product, version);
return -EINVAL;
}

@@ -509,7 +539,7 @@ static int acerhdf_register_platform(void)
{
int err = 0;

- err = platform_driver_register(&acerhdf_drv);
+ err = platform_driver_register(&acerhdf_driver);
if (err)
return err;

@@ -525,7 +555,7 @@ static void acerhdf_unregister_platform(void)
return;

platform_device_del(acerhdf_dev);
- platform_driver_unregister(&acerhdf_drv);
+ platform_driver_unregister(&acerhdf_driver);
}

static int acerhdf_register_thermal(void)

2009-07-08 20:10:57

by Peter Kästle

[permalink] [raw]
Subject: Re: [PATCH] Acerhdf: 0.5.15-2

Hi Boris,

found a little Bug, the "strncmp" in BIOS detection was using the wrong length, here's a new patch...

--peter

On Tue, 7 Jul 2009 22:07:56 +0200
Peter Feuerer <[email protected]> wrote:

> Hi Boris,
>
> On Mon, 6 Jul 2009 09:43:42 +0200
> Borislav Petkov <[email protected]> wrote:
>
> > The thing I'm observing here with the 0x21 command is that whenever it
> > is written into the ->fanreg, the fan gets turned on for maybe a second
> > or less and then off again. The 0x20 keeps it off just fine. What does
> > your BIOS version string say, here it is:
> >
> > [33340.535861] acerhdf: Acer Aspire One Fan driver, v.0.5.14
> > [33340.537078] acerhdf: BIOS info: Acer v0.3309, product: AOA150
>
> I tested your patch with value 0x20 on my machine ("AOA110" product, "v0.3309" version).
> The fan was freaking out and spinning high like a helicopter.
>
> I think we have to distinguish between the BIOS products too. Here a patch, please have a look at it and test it.
>
> cheers,
> peter

Peter:
o Applied Borislav Petkov's patch
o Added BIOS product to BIOS table as AOA110 and AOA150 have
different register values
o Added force_product parameter to allow forcing different
product
o fixed linker warning caused by "acerhdf_drv" not being named
"acerhdf_driver"

Signed-off-by: Peter Feuerer <[email protected]>
---
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index bdfee17..388622c 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -52,7 +52,7 @@
*/
#undef START_IN_KERNEL_MODE

-#define DRV_VER "0.5.13"
+#define DRV_VER "0.5.15"

/*
* According to the Atom N270 datasheet,
@@ -90,6 +90,7 @@ static unsigned int fanoff = 58;
static unsigned int verbose;
static unsigned int fanstate = ACERHDF_FAN_AUTO;
static char force_bios[16];
+static char force_product[16];
static unsigned int prev_interval;
struct thermal_zone_device *thz_dev;
struct thermal_cooling_device *cl_dev;
@@ -107,34 +108,54 @@ module_param(verbose, uint, 0600);
MODULE_PARM_DESC(verbose, "Enable verbose dmesg output");
module_param_string(force_bios, force_bios, 16, 0);
MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check");
+module_param_string(force_product, force_product, 16, 0);
+MODULE_PARM_DESC(force_product, "Force BIOS product and omit BIOS check");
+
+/*
+ * cmd_off: to switch the fan completely off / to check if the fan is off
+ * cmd_auto: to set the BIOS in control of the fan. The BIOS regulates then
+ * the fan speed depending on the temperature
+ */
+struct fancmd {
+ u8 cmd_off;
+ u8 cmd_auto;
+};

/* BIOS settings */
struct bios_settings_t {
const char *vendor;
+ const char *product;
const char *version;
unsigned char fanreg;
unsigned char tempreg;
- unsigned char fancmd[2]; /* fan off and auto commands */
+ struct fancmd cmd;
};

/* Register addresses and values for different BIOS versions */
static const struct bios_settings_t bios_tbl[] = {
- {"Acer", "v0.3109", 0x55, 0x58, {0x1f, 0x00} },
- {"Acer", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
- {"Acer", "v0.3301", 0x55, 0x58, {0xaf, 0x00} },
- {"Acer", "v0.3304", 0x55, 0x58, {0xaf, 0x00} },
- {"Acer", "v0.3305", 0x55, 0x58, {0xaf, 0x00} },
- {"Acer", "v0.3308", 0x55, 0x58, {0x21, 0x00} },
- {"Acer", "v0.3309", 0x55, 0x58, {0x21, 0x00} },
- {"Acer", "v0.3310", 0x55, 0x58, {0x21, 0x00} },
- {"Gateway", "v0.3103", 0x55, 0x58, {0x21, 0x00} },
- {"Packard Bell", "v0.3105", 0x55, 0x58, {0x21, 0x00} },
- {"", "", 0, 0, {0, 0} }
+ /* working for AOA110 and AOA150 and other */
+ {"Acer", "AO", "v0.3109", 0x55, 0x58, {0x1f, 0x00} },
+ {"Acer", "AO", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
+ {"Acer", "AO", "v0.3301", 0x55, 0x58, {0xaf, 0x00} },
+ {"Acer", "AO", "v0.3304", 0x55, 0x58, {0xaf, 0x00} },
+ {"Acer", "AO", "v0.3305", 0x55, 0x58, {0xaf, 0x00} },
+ {"Acer", "AO", "v0.3308", 0x55, 0x58, {0x21, 0x00} },
+ /* AOA110 */
+ {"Acer", "AOA110", "v0.3309", 0x55, 0x58, {0x21, 0x00} },
+ {"Acer", "AOA110", "v0.3310", 0x55, 0x58, {0x21, 0x00} },
+ /* AOA150 */
+ {"Acer", "AOA150", "v0.3309", 0x55, 0x58, {0x20, 0x00} },
+ {"Acer", "AOA150", "v0.3310", 0x55, 0x58, {0x20, 0x00} },
+ /* special BIOS / other */
+ {"Gateway", "AOA110", "v0.3103", 0x55, 0x58, {0x21, 0x00} },
+ {"Packard Bell", "DOA150", "v0.3104", 0x55, 0x58, {0x21, 0x00} },
+ {"Packard Bell", "AOA110", "v0.3105", 0x55, 0x58, {0x21, 0x00} },
+ /* pewpew-terminator */
+ {"", "", "", 0, 0, {0, 0} }
};

static const struct bios_settings_t *bios_cfg __read_mostly;

-
static int acerhdf_get_temp(int *temp)
{
u8 read_temp;
@@ -150,13 +171,14 @@ static int acerhdf_get_temp(int *temp)
static int acerhdf_get_fanstate(int *state)
{
u8 fan;
- bool tmp;

if (ec_read(bios_cfg->fanreg, &fan))
return -EINVAL;

- tmp = (fan == bios_cfg->fancmd[ACERHDF_FAN_OFF]);
- *state = tmp ? ACERHDF_FAN_OFF : ACERHDF_FAN_AUTO;
+ if (fan != bios_cfg->cmd.cmd_off)
+ *state = ACERHDF_FAN_AUTO;
+ else
+ *state = ACERHDF_FAN_OFF;

return 0;
}
@@ -175,7 +197,8 @@ static void acerhdf_change_fanstate(int state)
state = ACERHDF_FAN_AUTO;
}

- cmd = bios_cfg->fancmd[state];
+ cmd = (state == ACERHDF_FAN_OFF) ? bios_cfg->cmd.cmd_off
+ : bios_cfg->cmd.cmd_auto;
fanstate = state;

ec_write(bios_cfg->fanreg, cmd);
@@ -437,7 +460,7 @@ static int acerhdf_remove(struct platform_device *device)
return 0;
}

-struct platform_driver acerhdf_drv = {
+static struct platform_driver acerhdf_driver = {
.driver = {
.name = "acerhdf",
.owner = THIS_MODULE,
@@ -454,32 +477,40 @@ static int acerhdf_check_hardware(void)
{
char const *vendor, *version, *product;
int i;
+ unsigned long prod_len = 0;

/* get BIOS data */
vendor = dmi_get_system_info(DMI_SYS_VENDOR);
version = dmi_get_system_info(DMI_BIOS_VERSION);
product = dmi_get_system_info(DMI_PRODUCT_NAME);

+
pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER);

- if (!force_bios[0]) {
- if (strncmp(product, "AO", 2)) {
- pr_err("no Aspire One hardware found\n");
- return -EINVAL;
- }
- } else {
- pr_info("forcing BIOS version: %s\n", version);
+ if (force_bios[0]) {
version = force_bios;
+ pr_info("forcing BIOS version: %s\n", version);
kernelmode = 0;
}

+ if (force_product[0]) {
+ product = force_product;
+ pr_info("forcing BIOS product: %s\n", product);
+ kernelmode = 0;
+ }
+
+ prod_len = strlen(product);
+
if (verbose)
pr_info("BIOS info: %s %s, product: %s\n",
vendor, version, product);

/* search BIOS version and vendor in BIOS settings table */
for (i = 0; bios_tbl[i].version[0]; i++) {
- if (!strcmp(bios_tbl[i].vendor, vendor) &&
+ if (strlen(bios_tbl[i].product) >= prod_len &&
+ !strncmp(bios_tbl[i].product, product,
+ strlen(bios_tbl[i].product)) &&
+ !strcmp(bios_tbl[i].vendor, vendor) &&
!strcmp(bios_tbl[i].version, version)) {
bios_cfg = &bios_tbl[i];
break;
@@ -487,8 +518,8 @@ static int acerhdf_check_hardware(void)
}

if (!bios_cfg) {
- pr_err("unknown (unsupported) BIOS version %s/%s, "
- "please report, aborting!\n", vendor, version);
+ pr_err("unknown (unsupported) BIOS version %s/%s/%s, "
+ "please report, aborting!\n", vendor, product, version);
return -EINVAL;
}

@@ -509,7 +540,7 @@ static int acerhdf_register_platform(void)
{
int err = 0;

- err = platform_driver_register(&acerhdf_drv);
+ err = platform_driver_register(&acerhdf_driver);
if (err)
return err;

@@ -525,7 +556,7 @@ static void acerhdf_unregister_platform(void)
return;

platform_device_del(acerhdf_dev);
- platform_driver_unregister(&acerhdf_drv);
+ platform_driver_unregister(&acerhdf_driver);
}

static int acerhdf_register_thermal(void)