blob: 01953a682ffb1b516ee702c5907f4487bd8b6c89 [file] [log] [blame]
/*
* platform_sdio_regulator.c: sdio regulator platform device initilization file
*
* (C) Copyright 2011 Intel Corporation
* Author: chuanxiao.dong@intel.com, feiyix.ning@intel.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; version 2
* of the License.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <asm/intel-mid.h>
#include <linux/gpio.h>
#include <linux/lnw_gpio.h>
#include <linux/delay.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/fixed.h>
#include <linux/acpi.h>
#include <linux/acpi_gpio.h>
#define DELAY_ONOFF 250
struct acpi_ids { char *hid; char *uid; };
static struct acpi_ids intel_sdio_ids[] = {
{"INT33BB", "2"}, /* BYT SDIO */
{ },
};
static struct acpi_ids intel_brc_ids[] = {
{"BCM4321", NULL}, /* BYT SDIO */
{ },
};
static struct regulator_consumer_supply wlan_vmmc_supply = {
.supply = "vmmc",
};
static struct regulator_init_data wlan_vmmc_data = {
.constraints = {
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = 1,
.consumer_supplies = &wlan_vmmc_supply,
};
static struct fixed_voltage_config vwlan = {
.supply_name = "wlan_en_acpi",
.microvolts = 1800000,
.gpio = -EINVAL,
.startup_delay = 1000 * DELAY_ONOFF,
.enable_high = 1,
.enabled_at_boot = 0,
.init_data = &wlan_vmmc_data,
};
static void vwlan_device_release(struct device *dev) {}
static struct platform_device vwlan_device = {
.name = "reg-fixed-voltage",
.id = PLATFORM_DEVID_AUTO,
.dev = {
.platform_data = &vwlan,
.release = vwlan_device_release,
},
};
static struct acpi_device *acpi_bus_get_parent(acpi_handle handle)
{
struct acpi_device *device = NULL;
acpi_status status;
int result;
struct acpi_device *acpi_root;
result = acpi_bus_get_device(ACPI_ROOT_OBJECT, &acpi_root);
if (result)
return NULL;
/*
* Fixed hardware devices do not appear in the namespace and do not
* have handles, but we fabricate acpi_devices for them, so we have
* to deal with them specially.
*/
if (!handle)
return acpi_root;
do {
status = acpi_get_parent(handle, &handle);
if (ACPI_FAILURE(status))
return status == AE_NULL_ENTRY ? NULL : acpi_root;
} while (acpi_bus_get_device(handle, &device));
return device;
}
static int sdio_acpi_match(struct device *dev, void *data)
{
struct acpi_ids *ids = data;
struct acpi_handle *handle = ACPI_HANDLE(dev);
struct acpi_device_info *info;
char *uid = NULL;
acpi_status status;
status = acpi_get_object_info(handle, &info);
if (!ACPI_FAILURE(status) && (info->valid & ACPI_VALID_UID))
uid = info->unique_id.string;
else
return false;
if (!strncmp(ids->hid, dev_name(dev), strlen(ids->hid)))
if (!strcmp(ids->uid, uid))
return true;
return false;
}
static int brc_acpi_match(struct device *dev, void *data)
{
struct acpi_ids *ids = data;
if (!strncmp(ids->hid, dev_name(dev), strlen(ids->hid)))
return true;
return false;
}
static int brc_fixed_regulator_register_by_acpi(struct platform_device *pdev)
{
struct device *dev;
struct acpi_ids *brc_ids;
struct fixed_voltage_config *fixedcfg = NULL;
struct regulator_init_data *data = NULL;
struct acpi_handle *handle;
struct acpi_device *parent;
if (!pdev)
return -ENODEV;
fixedcfg = pdev->dev.platform_data;
if (!fixedcfg)
return -ENODEV;
data = fixedcfg->init_data;
if (!data || !data->consumer_supplies)
return -ENODEV;
/* get the GPIO pin from ACPI device first */
for (brc_ids = intel_brc_ids; brc_ids->hid; brc_ids++) {
dev = bus_find_device(&platform_bus_type, NULL,
brc_ids, brc_acpi_match);
if (dev) {
handle = ACPI_HANDLE(dev);
if (!ACPI_HANDLE(dev))
continue;
parent = acpi_bus_get_parent(handle);
if (!parent)
continue;
data->consumer_supplies->dev_name =
dev_name(&parent->dev);
fixedcfg->gpio = acpi_get_gpio_by_index(dev, 1, NULL);
if (fixedcfg->gpio < 0) {
dev_info(dev, "No wlan-enable GPIO\n");
continue;
}
dev_info(dev, "wlan-enable GPIO %d found\n",
fixedcfg->gpio);
break;
}
}
if (brc_ids->hid) {
/* add a regulator to control wlan enable gpio */
return platform_device_register(&vwlan_device);
}
return -ENODEV;
}
static int sdio_fixed_regulator_register_by_acpi(struct platform_device *pdev)
{
struct device *dev;
struct acpi_ids *sdio_ids;
struct fixed_voltage_config *fixedcfg = NULL;
struct regulator_init_data *data = NULL;
if (!pdev)
return -ENODEV;
fixedcfg = pdev->dev.platform_data;
if (!fixedcfg)
return -ENODEV;
data = fixedcfg->init_data;
if (!data || !data->consumer_supplies)
return -ENODEV;
/* get the GPIO pin from ACPI device first */
for (sdio_ids = intel_sdio_ids; sdio_ids->hid; sdio_ids++) {
dev = bus_find_device(&platform_bus_type, NULL,
sdio_ids, sdio_acpi_match);
if (dev) {
data->consumer_supplies->dev_name = dev_name(dev);
fixedcfg->gpio = acpi_get_gpio_by_index(dev, 0, NULL);
if (fixedcfg->gpio < 0) {
dev_info(dev, "No wlan-enable GPIO\n");
continue;
}
dev_info(dev, "wlan-enable GPIO %d found\n",
fixedcfg->gpio);
break;
}
}
if (sdio_ids->hid) {
/* add a regulator to control wlan enable gpio */
return platform_device_register(&vwlan_device);
}
return -ENODEV;
}
static int __init wifi_regulator_init(void)
{
int ret;
/* register fixed regulator through ACPI device */
ret = brc_fixed_regulator_register_by_acpi(&vwlan_device);
if (!ret)
return ret;
pr_err("%s: No SDIO host in platform devices\n", __func__);
return ret;
}
rootfs_initcall(wifi_regulator_init);