lkml.org 
[lkml]   [2011]   [Jan]   [7]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    Date
    SubjectRe: [PATCH V2] platform-driver-x86: ACPI EC Extra driver for Oaktrail
    From
    On Fri, Jan 7, 2011 at 8:41 AM, Yin Kangkai <kangkai.yin@linux.intel.com> wrote:
    > Patch V2
    > Changes from V1:
    >  - remove wifi, bt, gps, wwan from /sys/devices/platform.
    >  - add some helper function for rfkill alloc and rfkill cleanup.
    >  - add MODULE_ALIAS.
    >  - add Documentation/ABI/testing/sysfs-platform-intel-oaktrail
    >  - various other minor changes.
    >  - bump driver version from 0.1 to 0.2
    >
    > Thanks Corentin Chary and Joey Lee for the review.
    >
    > From ab28d41e89b40e0ab1d73489b7d1a9f6e745c14a Mon Sep 17 00:00:00 2001
    > From: Yin Kangkai <kangkai.yin@intel.com>
    > Date: Wed, 22 Dec 2010 10:53:36 +0800
    > Subject: [PATCH] platform-driver-x86: ACPI EC Extra driver for Oaktrail
    >
    > This driver implements an Extra ACPI EC driver for products based on Intel
    > Oaktrail platform.  It is programming the EC space, through existing ACPI EC
    > driver, to provide user space layer the sysfs and rfkill interfaces to
    > enable/disable the Camera, Bluetooth, GPS, WiFi, 3G, and to show the status of
    > Touchscreen.
    >
    > Signed-off-by: Yin Kangkai <kangkai.yin@intel.com>
    > ---
    >  .../ABI/testing/sysfs-platform-intel-oaktrail      |   13 +
    >  drivers/platform/x86/Kconfig                       |    9 +
    >  drivers/platform/x86/Makefile                      |    1 +
    >  drivers/platform/x86/intel_oaktrail.c              |  347 ++++++++++++++++++++
    >  4 files changed, 370 insertions(+), 0 deletions(-)
    >  create mode 100644 Documentation/ABI/testing/sysfs-platform-intel-oaktrail
    >  create mode 100644 drivers/platform/x86/intel_oaktrail.c
    >
    > diff --git a/Documentation/ABI/testing/sysfs-platform-intel-oaktrail b/Documentation/ABI/testing/sysfs-platform-intel-oaktrail
    > new file mode 100644
    > index 0000000..0512235
    > --- /dev/null
    > +++ b/Documentation/ABI/testing/sysfs-platform-intel-oaktrail
    > @@ -0,0 +1,13 @@
    > +What:          /sys/devices/platform/intel_oaktrail/camera
    > +Date:          Jan 2011
    > +KernelVersion: 2.6.37
    > +Contact:       "Yin Kangkai" <kangkai.yin@intel.com>
    > +Description:
    > +               Control the camera. 1 means on, 0 means off.
    > +
    > +What:          /sys/devices/platform/intel_oaktrail/touchscreen
    > +Date:          Jan 2011
    > +KernelVersion: 2.6.37
    > +Contact:       "Yin Kangkai" <kangkai.yin@intel.com>
    > +Description:
    > +               Show the status of the touch screen. 1 means on, 0 means off.
    > diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
    > index 2b4038a..d1f6981 100644
    > --- a/drivers/platform/x86/Kconfig
    > +++ b/drivers/platform/x86/Kconfig
    > @@ -655,4 +655,13 @@ config XO1_RFKILL
    >          Support for enabling/disabling the WLAN interface on the OLPC XO-1
    >          laptop.
    >
    > +config INTEL_OAKTRAIL
    > +       tristate "Intel Oaktrail Platform Extras"
    > +       depends on ACPI
    > +       depends on RFKILL
    > +       ---help---
    > +         Intel Oaktrail platform need this driver to provide interfaces to
    > +         enable/disable the Camera, WiFi, BT etc. devices. If in doubt, say Y
    > +         here; it will only load on supported platforms.
    > +
    >  endif # X86_PLATFORM_DEVICES
    > diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
    > index 7ff60e6..add8ab7 100644
    > --- a/drivers/platform/x86/Makefile
    > +++ b/drivers/platform/x86/Makefile
    > @@ -35,3 +35,4 @@ obj-$(CONFIG_INTEL_IPS)               += intel_ips.o
    >  obj-$(CONFIG_GPIO_INTEL_PMIC)  += intel_pmic_gpio.o
    >  obj-$(CONFIG_XO1_RFKILL)       += xo1-rfkill.o
    >  obj-$(CONFIG_IBM_RTL)          += ibm_rtl.o
    > +obj-$(CONFIG_INTEL_OAKTRAIL)   += intel_oaktrail.o
    > diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c
    > new file mode 100644
    > index 0000000..9ec71c5
    > --- /dev/null
    > +++ b/drivers/platform/x86/intel_oaktrail.c
    > @@ -0,0 +1,347 @@
    > +/*
    > +  Copyright (C) 2010 Intel Corporation
    > +  Author: Yin Kangkai (kangkai.yin@intel.com)
    > +
    > +  based on Compal driver
    > +
    > +  Copyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com>
    > +
    > +  based on MSI driver
    > +
    > +  Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
    > +
    > +  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., 51 Franklin Street, Fifth Floor, Boston, MA
    > +  02110-1301, USA.
    > + */
    > +
    > +/*
    > + * intel_oaktrail.c - Intel OakTrail Platform support.
    > + *
    > + * This driver exports a few files in /sys/devices/platform/intel_oaktrail/:
    > + *
    > + * camera - Camera subsystem enabled: contains either 0 or 1. (rw)
    > + * touchscreen - Touchscreen subsystem enabled: contains either 0 or 1. (ro)
    > + *
    > + * In addition to these platform device attributes, the driver registers itself
    > + * in the Linux rfkill subsystem for these components:
    > + *
    > + * wifi
    > + * bluetooth
    > + * wwan (3g)
    > + * gps
    > + *
    > + * and is available to userspace under /sys/class/rfkill/rfkillX/
    > + *
    > + * This driver might work on other products based on Oaktrail. If you
    > + * want to try it you can pass force=1 as argument to the module which
    > + * will force it to load even when the DMI data doesn't identify the
    > + * product as compatible.
    > + */
    > +
    > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
    > +
    > +#include <linux/module.h>
    > +#include <linux/kernel.h>
    > +#include <linux/init.h>
    > +#include <linux/acpi.h>
    > +#include <linux/platform_device.h>
    > +#include <linux/dmi.h>
    > +#include <linux/rfkill.h>
    > +
    > +#define DRIVER_NAME    "intel_oaktrail"
    > +#define DRIVER_VERSION "0.2"
    > +
    > +/*
    > + * This is the devices status address in EC space, and the control bits
    > + * definition:
    > + *
    > + * (1 << 0):   Camera enable/disable, RW.
    > + * (1 << 1):   Bluetooth enable/disable, RW.
    > + * (1 << 2):   GPS enable/disable, RW.
    > + * (1 << 3):   WiFi enable/disable, RW.
    > + * (1 << 4):   WWAN (3G) enable/disalbe, RW.
    > + * (1 << 5):   Touchscreen enable/disable, Read Only.
    > + */
    > +#define OT_EC_DEVICE_STATE_ADDRESS     0xD6
    > +
    > +#define OT_EC_CAMERA_MASK      (1 << 0)
    > +#define OT_EC_BT_MASK          (1 << 1)
    > +#define OT_EC_GPS_MASK         (1 << 2)
    > +#define OT_EC_WIFI_MASK                (1 << 3)
    > +#define OT_EC_WWAN_MASK                (1 << 4)
    > +#define OT_EC_TS_MASK          (1 << 5)
    > +
    > +static int force;
    > +module_param(force, bool, 0);
    > +MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
    > +
    > +static struct platform_device *oaktrail_device;
    > +static struct rfkill *bt_rfkill;
    > +static struct rfkill *gps_rfkill;
    > +static struct rfkill *wifi_rfkill;
    > +static struct rfkill *wwan_rfkill;
    > +
    > +#define SIMPLE_MASKED_STORE_SHOW(NAME, MASK)                           \
    > +static ssize_t NAME##_show(struct device *dev,                         \
    > +       struct device_attribute *attr, char *buf)                       \
    > +{                                                                      \
    > +       u8 value;                                                       \
    > +       ec_read(OT_EC_DEVICE_STATE_ADDRESS, &value);                    \
    > +       return sprintf(buf, "%d\n", ((value & MASK) != 0));             \
    > +}                                                                      \
    > +static ssize_t NAME##_store(struct device *dev,                                \
    > +       struct device_attribute *attr, const char *buf, size_t count)   \
    > +{                                                                      \
    > +       int state;                                                      \
    > +       u8 old_val;                                                     \
    > +       ec_read(OT_EC_DEVICE_STATE_ADDRESS, &old_val);                  \
    > +       if (sscanf(buf, "%d", &state) != 1 || (state < 0 || state > 1)) \
    > +               return -EINVAL;                                         \
    > +       ec_write(OT_EC_DEVICE_STATE_ADDRESS, state ?                    \
    > +                (old_val | MASK) : (old_val & ~MASK));                 \
    > +       return count;                                                   \
    > +}
    > +
    > +SIMPLE_MASKED_STORE_SHOW(camera, OT_EC_CAMERA_MASK)
    > +SIMPLE_MASKED_STORE_SHOW(touchscreen, OT_EC_TS_MASK)
    > +
    > +static DEVICE_ATTR(camera, 0644, camera_show, camera_store);
    > +static DEVICE_ATTR(touchscreen, 0444, touchscreen_show, NULL);
    > +
    > +static struct attribute *oaktrail_attributes[] = {
    > +       &dev_attr_camera.attr,
    > +       &dev_attr_touchscreen.attr,
    > +       NULL
    > +};
    > +
    > +static struct attribute_group oaktrail_attribute_group = {
    > +       .attrs = oaktrail_attributes
    > +};
    > +
    > +static int oaktrail_rfkill_set(void *data, bool blocked)
    > +{
    > +       u8 value;
    > +       u8 result;
    > +       unsigned long radio = (unsigned long) data;
    > +
    > +       ec_read(OT_EC_DEVICE_STATE_ADDRESS, &result);
    > +
    > +       if (!blocked)
    > +               value = (u8) (result | radio);
    > +       else
    > +               value = (u8) (result & ~radio);
    > +
    > +       ec_write(OT_EC_DEVICE_STATE_ADDRESS, value);
    > +
    > +       return 0;
    > +}
    > +
    > +static const struct rfkill_ops oaktrail_rfkill_ops = {
    > +       .set_block = oaktrail_rfkill_set,
    > +};
    > +
    > +static struct rfkill *oaktrail_rfkill_new(char *name, enum rfkill_type type,
    > +                                         unsigned long mask)
    > +{
    > +       int err;
    > +       struct rfkill *rfkill_dev;
    > +
    > +       rfkill_dev = rfkill_alloc(name, &oaktrail_device->dev, type,
    > +                                 &oaktrail_rfkill_ops, (void *)mask);
    > +       if (!rfkill_dev)
    > +               return ERR_PTR(-ENOMEM);
    > +
    > +       err = rfkill_register(rfkill_dev);

    Maybe you should add a rfkill_init_sw_state() call here, to be sure
    that the rfkill initial state is right.

    > +       if (err) {
    > +               rfkill_destroy(rfkill_dev);
    > +               return ERR_PTR(err);
    > +       }
    > +       return rfkill_dev;
    > +}
    > +
    > +static inline void _oaktrail_rfkill_cleanup(struct rfkill *rf)

    I don't know if the coding style allow _ function prefix, I used to
    believe it did
    not, but I checked Documentation/CodingStyle, and I found nothing about that,
    so it's probably ok.

    > +{
    > +       if (rf) {
    > +               rfkill_unregister(rf);
    > +               rfkill_destroy(rf);
    > +       }
    > +
    > +       return;

    We don't need that return

    > +}
    > +
    > +static void oaktrail_rfkill_cleanup(void)
    > +{
    > +       _oaktrail_rfkill_cleanup(wifi_rfkill);
    > +       _oaktrail_rfkill_cleanup(bt_rfkill);
    > +       _oaktrail_rfkill_cleanup(gps_rfkill);
    > +       _oaktrail_rfkill_cleanup(wwan_rfkill);
    > +
    > +       return;

    And that one

    > +}
    > +
    > +static int oaktrail_rfkill_init(void)
    > +{
    > +       int ret;
    > +
    > +       wifi_rfkill = oaktrail_rfkill_new("oaktrail-wifi",
    > +                                         RFKILL_TYPE_WLAN,
    > +                                         OT_EC_WIFI_MASK);
    > +       if (IS_ERR(wifi_rfkill)) {
    > +               ret = PTR_ERR(wifi_rfkill);
    > +               wifi_rfkill = NULL;
    > +               goto cleanup;
    > +       }
    > +
    > +       bt_rfkill = oaktrail_rfkill_new("oaktrail-bluetooth",
    > +                                       RFKILL_TYPE_BLUETOOTH,
    > +                                       OT_EC_BT_MASK);
    > +       if (IS_ERR(bt_rfkill)) {
    > +               ret = PTR_ERR(bt_rfkill);
    > +               bt_rfkill = NULL;
    > +               goto cleanup;
    > +       }
    > +
    > +       gps_rfkill = oaktrail_rfkill_new("oaktrail-gps",
    > +                                        RFKILL_TYPE_GPS,
    > +                                        OT_EC_GPS_MASK);
    > +       if (IS_ERR(gps_rfkill)) {
    > +               ret = PTR_ERR(gps_rfkill);
    > +               gps_rfkill = NULL;
    > +               goto cleanup;
    > +       }
    > +
    > +       wwan_rfkill = oaktrail_rfkill_new("oaktrail-wwan",
    > +                                         RFKILL_TYPE_WWAN,
    > +                                         OT_EC_WWAN_MASK);
    > +       if (IS_ERR(wwan_rfkill)) {
    > +               ret = PTR_ERR(wwan_rfkill);
    > +               wwan_rfkill = NULL;
    > +               goto cleanup;
    > +       }
    > +
    > +       return 0;
    > +
    > +cleanup:
    > +       oaktrail_rfkill_cleanup();
    > +       return ret;
    > +}
    > +
    > +static int __devinit oaktrail_probe(struct platform_device *pdev)
    > +{
    > +       return sysfs_create_group(&pdev->dev.kobj, &oaktrail_attribute_group);
    > +}
    > +
    > +static int __devexit oaktrail_remove(struct platform_device *pdev)
    > +{
    > +       sysfs_remove_group(&pdev->dev.kobj, &oaktrail_attribute_group);
    > +
    > +       return 0;
    > +}
    > +
    > +static struct platform_driver oaktrail_driver = {
    > +       .driver = {
    > +               .name = DRIVER_NAME,
    > +               .owner = THIS_MODULE,
    > +       },
    > +       .probe  = oaktrail_probe,
    > +       .remove = __devexit_p(oaktrail_remove)
    > +};
    > +
    > +static int dmi_check_cb(const struct dmi_system_id *id)
    > +{
    > +       pr_info("Identified model '%s'\n", id->ident);
    > +       return 0;
    > +}
    > +
    > +static struct dmi_system_id __initdata oaktrail_dmi_table[] = {
    > +       {
    > +               .ident = "OakTrail platform",
    > +               .matches = {
    > +                       DMI_MATCH(DMI_PRODUCT_NAME, "OakTrail platform"),
    > +               },
    > +               .callback = dmi_check_cb
    > +       },
    > +       { }
    > +};
    > +
    > +static int __init oaktrail_init(void)
    > +{
    > +       int ret;
    > +
    > +       if (acpi_disabled) {
    > +               pr_err("ACPI needs to be enabled for this driver to work!\n");
    > +               return -ENODEV;
    > +       }
    > +
    > +       if (!force && !dmi_check_system(oaktrail_dmi_table)) {
    > +               pr_err("Platform not recognized (You could try the module's force-parameter)");
    > +               return -ENODEV;
    > +       }
    > +
    > +       ret = platform_driver_register(&oaktrail_driver);
    > +       if (ret) {
    > +               pr_warning("Unable to register platform driver\n");
    > +               goto err_driver_reg;
    > +       }
    > +
    > +       oaktrail_device = platform_device_alloc(DRIVER_NAME, -1);
    > +       if (!oaktrail_device) {
    > +               pr_warning("Unable to allocate platform device\n");
    > +               ret = -ENOMEM;
    > +               goto err_device_alloc;
    > +       }
    > +
    > +       ret = platform_device_add(oaktrail_device);
    > +       if (ret) {
    > +               pr_warning("Unable to add platform device\n");
    > +               goto err_device_add;
    > +       }
    > +
    > +       ret = oaktrail_rfkill_init();
    > +       if (ret) {
    > +               pr_warning("Setup rfkill failed\n");
    > +               goto err_rfkill;
    > +       }
    > +
    > +       pr_info("Driver "DRIVER_VERSION" successfully loaded\n");
    > +       return 0;
    > +
    > +err_rfkill:
    > +       platform_device_del(oaktrail_device);
    > +err_device_add:
    > +       platform_device_put(oaktrail_device);
    > +err_device_alloc:
    > +       platform_driver_unregister(&oaktrail_driver);
    > +err_driver_reg:
    > +       return ret;
    > +}
    > +
    > +static void __exit oaktrail_cleanup(void)
    > +{
    > +       platform_device_unregister(oaktrail_device);
    > +       platform_driver_unregister(&oaktrail_driver);
    > +       oaktrail_rfkill_cleanup();
    > +
    > +       pr_info("Driver unloaded\n");
    > +}
    > +
    > +module_init(oaktrail_init);
    > +module_exit(oaktrail_cleanup);
    > +
    > +MODULE_AUTHOR("Yin Kangkai (kangkai.yin@intel.com)");
    > +MODULE_DESCRIPTION("Intel Oaktrail Platform ACPI Extras");
    > +MODULE_VERSION(DRIVER_VERSION);
    > +MODULE_LICENSE("GPL");
    > +MODULE_ALIAS("dmi:*:svnIntelCorporation:pnOakTrailplatform:*");
    > --
    > 1.6.5
    >
    >

    --
    Corentin Chary
    http://xf.iksaif.net
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

    \
     
     \ /
      Last update: 2011-01-07 09:59    [W:2.496 / U:0.164 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site