#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/rfkill.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
#include <linux/sunxi-gpio.h>
#include <linux/etherdevice.h>
#include <linux/miscdevice.h>
#include <linux/capability.h>
#include <linux/pm_wakeirq.h>
#include "sunxi-rfkill.h"
struct sunxi_wlan_platdata {
struct pinctrl *pctrl;
int gpio_red_led;
int gpio_green_led;
int gpio_blue_led;
int gpio_power_en;
struct platform_device *pdev;
};
static struct sunxi_wlan_platdata *wlan_data;
static int sunxi_wlan_on(struct sunxi_wlan_platdata *data, bool on_off);
static DEFINE_MUTEX(sunxi_wlan_mutex);
void sunxi_wl_poweren_set(int dev, int on_off)
{
/* Only wifi and bt both close, power_en goes down,
* otherwise, set power_en up to keep module work.
* dev : device to set power status. 0: wifi, 1: bt
* on_off: power status to set. 0: off, 1: on
*/
static int power_state;
if (dev == WL_DEV_WIFI || dev == WL_DEV_BLUETOOTH) {
power_state &= ~(1 << dev);
power_state |= ((on_off > 0) << dev);
}
if (gpio_is_valid(wlan_data->gpio_power_en))
gpio_set_value(wlan_data->gpio_power_en, (power_state != 0));
}
EXPORT_SYMBOL_GPL(sunxi_wl_poweren_set);
void sunxi_wlan_set_power(bool on_off)
{
struct platform_device *pdev;
int ret = 0;
if (!wlan_data)
return;
pdev = wlan_data->pdev;
mutex_lock(&sunxi_wlan_mutex);
sunxi_wl_poweren_set(WL_DEV_WIFI, on_off);
if (on_off != wlan_data->power_state) {
ret = sunxi_wlan_on(wlan_data, on_off);
if (ret)
dev_err(&pdev->dev, "set power failed\n");
}
sunxi_wl_chipen_set(WL_DEV_WIFI, on_off);
mutex_unlock(&sunxi_wlan_mutex);
}
EXPORT_SYMBOL_GPL(sunxi_wlan_set_power);
static int sunxi_wlan_on(struct sunxi_wlan_platdata *data, bool on_off)
{
struct platform_device *pdev = data->pdev;
struct device *dev = &pdev->dev;
int ret = 0;
if (!on_off && gpio_is_valid(data->gpio_wlan_regon))
gpio_set_value(data->gpio_wlan_regon, 0);
if (data->wlan_power_name) {
data->wlan_power = regulator_get(dev, data->wlan_power_name);
if (!IS_ERR(data->wlan_power)) {
if (on_off) {
ret = regulator_enable(data->wlan_power);
if (ret < 0) {
dev_err(dev, "regulator wlan_power enable failed\n");
regulator_put(data->wlan_power);
return ret;
}
ret = regulator_get_voltage(data->wlan_power);
if (ret < 0) {
dev_err(dev, "regulator wlan_power get voltage failed\n");
regulator_put(data->wlan_power);
return ret;
}
dev_info(dev, "check wlan wlan_power voltage: %d\n",
ret);
} else {
ret = regulator_disable(data->wlan_power);
if (ret < 0) {
dev_err(dev, "regulator wlan_power disable failed\n");
regulator_put(data->wlan_power);
return ret;
}
}
regulator_put(data->wlan_power);
}
}
if (data->io_regulator_name) {
data->io_regulator = regulator_get(dev,
data->io_regulator_name);
if (!IS_ERR(data->io_regulator)) {
if (on_off) {
ret = regulator_enable(data->io_regulator);
if (ret < 0) {
dev_err(dev, "regulator io_regulator enable failed\n");
regulator_put(data->io_regulator);
return ret;
}
ret = regulator_get_voltage(data->io_regulator);
if (ret < 0) {
dev_err(dev, "regulator io_regulator get voltage failed\n");
regulator_put(data->io_regulator);
return ret;
}
dev_info(dev, "check wlan io_regulator voltage: %d\n",
ret);
} else {
ret = regulator_disable(data->io_regulator);
if (ret < 0) {
dev_err(dev, "regulator io_regulator disable failed\n");
regulator_put(data->io_regulator);
return ret;
}
}
regulator_put(data->io_regulator);
}
}
if (on_off && gpio_is_valid(data->gpio_wlan_regon)) {
mdelay(10);
gpio_set_value(data->gpio_wlan_regon, 1);
}
wlan_data->power_state = on_off;
return 0;
}
static ssize_t power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", wlan_data->power_state);
}
static ssize_t power_state_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long state;
int err;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
err = kstrtoul(buf, 0, &state);
if (err)
return err;
if (state > 1)
return -EINVAL;
if (state != wlan_data->power_state) {
sunxi_wlan_set_power(state);
}
return count;
}
static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
power_state_show, power_state_store);
static ssize_t scan_device_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long state;
int err;
int bus = wlan_data->bus_index;
err = kstrtoul(buf, 0, &state);
if (err)
return err;
dev_info(dev, "start scan device on bus_index: %d\n",
wlan_data->bus_index);
if (bus < 0) {
dev_err(dev, "scan device fail!\n");
return -1;
}
sunxi_mmc_rescan_card(bus);
return count;
}
static DEVICE_ATTR(scan_device, S_IRUGO | S_IWUSR,
NULL, scan_device_store);
static struct attribute *misc_attributes[] = {
&dev_attr_power_state.attr,
&dev_attr_scan_device.attr,
NULL,
};
static struct attribute_group misc_attribute_group = {
.name = "rf-ctrl",
.attrs = misc_attributes,
};
static struct miscdevice sunxi_wlan_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "sunxi-led",
};
static int sunxi_wlan_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct sunxi_wlan_platdata *data;
struct gpio_config config;
int ret = 0;
// struct pinctrl_state *pctrl_state = NULL;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!dev)
return -ENOMEM;
data->pdev = pdev;
wlan_data = data;
//RED
data->gpio_red_led = of_get_named_gpio_flags(np, "h616_red",
0, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(data->gpio_red_led)) {
dev_err(dev, "get gpio gpio_red_led failed\n");
} else {
dev_info(dev, "wlan_regon gpio=%d mul-sel=%d pull=%d drv_level=%d data=%d\n",
config.gpio,
config.mul_sel,
config.pull,
config.drv_level,
config.data);
ret = devm_gpio_request(dev, data->gpio_red_led,
"h616_red");
if (ret < 0) {
dev_err(dev, "can't request gpio_red_led gpio %d\n",
data->gpio_red_led);
return ret;
}
ret = gpio_direction_output(data->gpio_red_led, 0);
if (ret < 0) {
dev_err(dev, "can't request output direction gpio_red_led gpio %d\n",
data->gpio_red_led);
return ret;
}
}
//GREEN
data->gpio_green_led = of_get_named_gpio_flags(np, "h616_green",
0, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(data->gpio_green_led)) {
dev_err(dev, "get gpio gpio_green_led failed\n");
} else {
dev_info(dev, "gpio_green_led gpio=%d mul-sel=%d pull=%d drv_level=%d data=%d\n",
config.gpio,
config.mul_sel,
config.pull,
config.drv_level,
config.data);
ret = devm_gpio_request(dev, data->gpio_green_led,
"h616_green");
if (ret < 0) {
dev_err(dev, "can't request gpio_green_led gpio %d\n",
data->gpio_green_led);
return ret;
}
ret = gpio_direction_output(data->gpio_green_led, 0);
if (ret < 0) {
dev_err(dev, "can't request output direction gpio_green_led gpio %d\n",
data->gpio_green_led);
return ret;
}
}
//BLUE
data->gpio_blue_led = of_get_named_gpio_flags(np, "h616_blue",
0, (enum of_gpio_flags *)&config);
if (!gpio_is_valid(data->gpio_blue_led)) {
dev_err(dev, "get gpio gpio_blue_led failed\n");
} else {
dev_info(dev, "gpio_blue_led gpio=%d mul-sel=%d pull=%d drv_level=%d data=%d\n",
config.gpio,
config.mul_sel,
config.pull,
config.drv_level,
config.data);
ret = devm_gpio_request(dev, data->gpio_blue_led,
"h616_blue");
if (ret < 0) {
dev_err(dev, "can't request gpio_blue_led gpio %d\n",
data->gpio_blue_led);
return ret;
}
ret = gpio_direction_output(data->gpio_blue_led, 0);
if (ret < 0) {
dev_err(dev, "can't request output direction gpio_blue_led gpio %d\n",
data->gpio_blue_led);
return ret;
}
}
//获取某个GPIO状态
// data->pctrl = devm_pinctrl_get(dev);
// if (IS_ERR(data->pctrl)) {
// dev_warn(dev, "devm_pinctrl_get() failed!\n");
// } else {
// pctrl_state = pinctrl_lookup_state(data->pctrl, pctrl_name);
// if (IS_ERR(pctrl_state)) {
// dev_warn(dev, "pinctrl_lookup_state(%s) failed! return %p \n",
// pctrl_name, pctrl_state);
// } else {
// ret = pinctrl_select_state(data->pctrl, pctrl_state);
// if (ret < 0) {
// dev_warn(dev, "pinctrl_select_state(%s) failed! return %d \n",
// pctrl_name, ret);
// }
// }
// }
ret = misc_register(&sunxi_wlan_dev);
if (ret) {
dev_err(dev, "sunxi-wlan register driver as misc device error!\n");
return ret;
}
ret = sysfs_create_group(&sunxi_wlan_dev.this_device->kobj,
&misc_attribute_group);
if (ret) {
dev_err(dev, "sunxi-wlan register sysfs create group failed!\n");
return ret;
}
return 0;
}
static int sunxi_wlan_remove(struct platform_device *pdev)
{
misc_deregister(&sunxi_wlan_dev);
sysfs_remove_group(&(sunxi_wlan_dev.this_device->kobj),
&misc_attribute_group);
return 0;
}
static const struct of_device_id sunxi_wlan_ids[] = {
{ .compatible = "allwinner,sunxi-led" },
{ /* Sentinel */ }
};
static struct platform_driver sunxi_wlan_driver = {
.probe = sunxi_wlan_probe,
.remove = sunxi_wlan_remove,
.driver = {
.owner = THIS_MODULE,
.name = "sunxi-led",
.of_match_table = sunxi_wlan_ids,
},
};
static int __init sunxi_h616_led_init(void)
{
return platform_driver_register(&sunxi_wlan_driver);
}
module_init(sunxi_h616_led_init);
static void __exit sunxi_h616_led_exit(void)
{
return platform_driver_unregister(&sunxi_wlan_driver);
}
module_exit(sunxi_h616_led_exit);
//module_platform_driver(sunxi_wlan_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("huangbenyan");
MODULE_DESCRIPTION("sunxi led driver");
MODULE_ALIAS("led");
dts配置
led:led {
compatible = "allwinner,sunxi-led";
h616_red = <&pio PH 6 1 0xffffffff 0xffffffff 0>;
h616_green = <&pio PH 7 1 0xffffffff 0xffffffff 0>;
h616_blue = <&pio PH 8 1 0xffffffff 0xffffffff 0>;
status = "okay";
}