blob: 6783689e1062626569bf17b53225ea886b5d3c11 [file] [log] [blame]
/*
* Copyright (c) 2013 Qualcomm Atheros, Inc.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef EXPORT_SYMTAB
#define EXPORT_SYMTAB
#endif
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/signal.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/init.h>
#include <linux/resource.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
#include <asm/types.h>
#include <asm/irq.h>
#include <asm/delay.h>
#include <asm/system.h>
#include <atheros.h>
#define ATH_FACTORY_RESET 0x89ABCDEF
static atomic_t ath_fr_status = ATOMIC_INIT(0);
static volatile int ath_fr_opened = 0;
static wait_queue_head_t ath_fr_wq;
#ifdef JUMPSTART_GPIO
static u_int32_t push_time = 0;
#ifdef CONFIG_MACH_QCA956x
static u_int32_t prev_push_time = 0;
#endif
#endif
struct timer_list os_timer_t;
static int initial_led_state = 0;
#ifdef POWER_ON_RLED_GPIO
#define POWER_ON_TIMEOUT 60 /* 60 * 0.5 seconds */
#define POWER_LED_BLINK_INTERVAL 500 /* microseconds */
static volatile int power_on_finish = 0;
struct timer_list power_on_timer;
static struct proc_dir_entry *power_on_proc_entry = NULL;
#endif
#define frdbg printk
#define WPS_LED_OFF 1
#define WPS_LED_ON 0
#define USB_LED_OFF 1
#define USB_LED_ON 0
#define POWER_LED_OFF 1
#define POWER_LED_ON 0
#define SIMPLE_CONFIG_OFF 1
#define SIMPLE_CONFIG_ON 2
#define SIMPLE_CONFIG_BLINK 3
#define SIMPLE_CONFIG_FAIL 4
#define OS_TIMER_FUNC(_fn) \
void _fn(unsigned long timer_arg)
#define OS_GET_TIMER_ARG(_arg, _type) \
(_arg) = (_type)(timer_arg)
#define OS_INIT_TIMER(_osdev, _timer, _fn, _arg) \
do { \
init_timer(_timer); \
(_timer)->function = (_fn); \
(_timer)->data = (unsigned long)(_arg); \
} while (0)
#define OS_SET_TIMER(_timer, _ms) \
mod_timer(_timer, jiffies + ((_ms)*HZ)/1000)
#define OS_CANCEL_TIMER(_timer) del_timer(_timer)
/*
* GPIO interrupt stuff
*/
typedef enum {
INT_TYPE_EDGE,
INT_TYPE_LEVEL,
} ath_gpio_int_type_t;
typedef enum {
INT_POL_ACTIVE_LOW,
INT_POL_ACTIVE_HIGH,
} ath_gpio_int_pol_t;
/*
** Simple Config stuff
*/
typedef irqreturn_t (*sc_callback_t) (int, void *, void *, void *);
/*
* Multiple Simple Config callback support
* For multiple radio scenarios, we need to post the button push to
* all radios at the same time. However, there is only 1 button, so
* we only have one set of GPIO callback pointers.
*
* Creating a structure that contains each callback, tagged with the
* name of the device registering the callback. The unregister routine
* will need to determine which element to "unregister", so the device
* name will have to be passed to unregister also
*/
typedef struct {
char *name;
sc_callback_t registered_cb;
void *cb_arg1;
void *cb_arg2;
} multi_callback_t;
/*
* Specific instance of the callback structure
*/
static multi_callback_t sccallback[3];
static volatile int ignore_pushbutton = 1;
static struct proc_dir_entry *simple_config_entry = NULL;
static struct proc_dir_entry *simulate_push_button_entry = NULL;
static struct proc_dir_entry *simple_config_led_entry = NULL;
static int wps_led_blinking = 0;
void ath_gpio_config_int(int gpio,
ath_gpio_int_type_t type,
ath_gpio_int_pol_t polarity)
{
u32 val;
/*
* allow edge sensitive/rising edge too
*/
if (type == INT_TYPE_LEVEL) {
/* level sensitive */
ath_reg_rmw_set(ATH_GPIO_INT_TYPE, (1 << gpio));
} else {
/* edge triggered */
val = ath_reg_rd(ATH_GPIO_INT_TYPE);
val &= ~(1 << gpio);
ath_reg_wr(ATH_GPIO_INT_TYPE, val);
}
if (polarity == INT_POL_ACTIVE_HIGH) {
ath_reg_rmw_set(ATH_GPIO_INT_POLARITY, (1 << gpio));
} else {
val = ath_reg_rd(ATH_GPIO_INT_POLARITY);
val &= ~(1 << gpio);
ath_reg_wr(ATH_GPIO_INT_POLARITY, val);
}
ath_reg_rmw_set(ATH_GPIO_INT_ENABLE, (1 << gpio));
}
void ath_gpio_config_output(int gpio)
{
#if defined(CONFIG_MACH_AR934x) || \
defined(CONFIG_MACH_QCA955x) || \
defined(CONFIG_MACH_QCA953x) || \
defined(CONFIG_MACH_QCA956x)
ath_reg_rmw_clear(ATH_GPIO_OE, (1 << gpio));
#else
ath_reg_rmw_set(ATH_GPIO_OE, (1 << gpio));
#endif
}
EXPORT_SYMBOL(ath_gpio_config_output);
void ath_gpio_config_input(int gpio)
{
#if defined(CONFIG_MACH_AR934x) || \
defined(CONFIG_MACH_QCA955x) || \
defined(CONFIG_MACH_QCA953x) || \
defined(CONFIG_MACH_QCA956x)
ath_reg_rmw_set(ATH_GPIO_OE, (1 << gpio));
#else
ath_reg_rmw_clear(ATH_GPIO_OE, (1 << gpio));
#endif
}
void ath_gpio_out_val(int gpio, int val)
{
if (val & 0x1) {
ath_reg_rmw_set(ATH_GPIO_OUT, (1 << gpio));
} else {
ath_reg_rmw_clear(ATH_GPIO_OUT, (1 << gpio));
}
}
EXPORT_SYMBOL(ath_gpio_out_val);
int ath_gpio_in_val(int gpio)
{
return ((1 << gpio) & (ath_reg_rd(ATH_GPIO_IN)));
}
static void
ath_gpio_intr_enable(unsigned int irq)
{
ath_reg_rmw_set(ATH_GPIO_INT_MASK,
(1 << (irq - ATH_GPIO_IRQ_BASE)));
}
static void
ath_gpio_intr_disable(unsigned int irq)
{
ath_reg_rmw_clear(ATH_GPIO_INT_MASK,
(1 << (irq - ATH_GPIO_IRQ_BASE)));
}
static unsigned int
ath_gpio_intr_startup(unsigned int irq)
{
ath_gpio_intr_enable(irq);
return 0;
}
static void
ath_gpio_intr_shutdown(unsigned int irq)
{
ath_gpio_intr_disable(irq);
}
static void
ath_gpio_intr_ack(unsigned int irq)
{
ath_gpio_intr_disable(irq);
}
static void
ath_gpio_intr_end(unsigned int irq)
{
if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
ath_gpio_intr_enable(irq);
}
static int
ath_gpio_intr_set_affinity(unsigned int irq, const struct cpumask *dest)
{
/*
* Only 1 CPU; ignore affinity request
*/
return 0;
}
struct irq_chip /* hw_interrupt_type */ ath_gpio_intr_controller = {
.name = "ATH GPIO",
.startup = ath_gpio_intr_startup,
.shutdown = ath_gpio_intr_shutdown,
.enable = ath_gpio_intr_enable,
.disable = ath_gpio_intr_disable,
.ack = ath_gpio_intr_ack,
.end = ath_gpio_intr_end,
.eoi = ath_gpio_intr_end,
.set_affinity = ath_gpio_intr_set_affinity,
};
void ath_gpio_irq_init(int irq_base)
{
int i;
for (i = irq_base; i < irq_base + ATH_GPIO_IRQ_COUNT; i++) {
irq_desc[i].status = IRQ_DISABLED;
irq_desc[i].action = NULL;
irq_desc[i].depth = 1;
//irq_desc[i].chip = &ath_gpio_intr_controller;
set_irq_chip_and_handler(i, &ath_gpio_intr_controller,
handle_percpu_irq);
}
}
void
ath_gpio_set_fn(int gpio, int fn)
{
#define gpio_fn_reg(x) ((x) / 4)
#define gpio_lsb(x) (((x) % 4) * 8)
#define gpio_msb(x) (gpio_lsb(x) + 7)
#define gpio_mask(x) (0xffu << gpio_lsb(x))
#define gpio_set(x, f) (((f) & 0xffu) << gpio_lsb(x))
uint32_t *reg = ((uint32_t *)GPIO_OUT_FUNCTION0_ADDRESS) +
gpio_fn_reg(gpio);
ath_reg_wr(reg, (ath_reg_rd(reg) & ~gpio_mask(gpio)) | gpio_set(gpio, fn));
}
int32_t register_simple_config_callback(char *cbname, void *callback, void *arg1, void *arg2)
{
int i;
int cbnum = sizeof(sccallback)/sizeof(multi_callback_t);
printk("SC Callback Registration for %s\n", cbname);
for (i = 0; i< cbnum; i++)
{
if (!sccallback[i].name) {
sccallback[i].name = (char*)kmalloc(strlen(cbname), GFP_KERNEL);
strcpy(sccallback[i].name, cbname);
sccallback[i].registered_cb = (sc_callback_t) callback;
sccallback[i].cb_arg1 = arg1;
sccallback[i].cb_arg2 = arg2;
break;
}
}
if (i == cbnum)
{
printk("@@@@ Failed SC Callback Registration for %s\n", cbname);
return -1;
}
return 0;
}
EXPORT_SYMBOL(register_simple_config_callback);
int32_t unregister_simple_config_callback(char *cbname)
{
int i;
int cbnum = sizeof(sccallback)/sizeof(multi_callback_t);
for (i = 0; i< cbnum; i++)
{
if (sccallback[i].name && strcmp(sccallback[i].name, cbname) == 0) {
kfree(sccallback[i].name);
sccallback[i].name = NULL;
sccallback[i].registered_cb = NULL;
sccallback[i].cb_arg1 = NULL;
sccallback[i].cb_arg2 = NULL;
break;
}
}
if (i == cbnum){
printk("!&!&!&!& ERROR: Unknown callback name %s\n", cbname);
return -1;
}
return 0;
}
EXPORT_SYMBOL(unregister_simple_config_callback);
#ifdef WPS_LED_GPIO
#define WPS_TIME_OUT 120
static OS_TIMER_FUNC(wps_led_blink)
{
static int WPSled = WPS_LED_ON, sec = 0;
ath_gpio_out_val(WPS_LED_GPIO, WPSled);
WPSled = !WPSled;
sec++;
if (sec < WPS_TIME_OUT) {
OS_SET_TIMER(&os_timer_t, 1000);
} else {
sec = 0;
wps_led_blinking = 0;
OS_CANCEL_TIMER(&os_timer_t);
ath_gpio_out_val(WPS_LED_GPIO, initial_led_state);
}
}
static OS_TIMER_FUNC(wps_led_fail)
{
static int WPSled = WPS_LED_ON, sec = 0;
ath_gpio_out_val(WPS_LED_GPIO, WPSled);
WPSled = !WPSled;
sec++;
if (sec < 250 * 5) {//Keep blinking for 250 seconds & timer callback kicks in every 200 ms
OS_SET_TIMER(&os_timer_t, 200);
} else {
sec = 0;
wps_led_blinking = 0;
OS_CANCEL_TIMER(&os_timer_t);
ath_gpio_out_val(WPS_LED_GPIO, initial_led_state);
}
}
static OS_TIMER_FUNC(wps_led_success)
{
wps_led_blinking = 0;
OS_CANCEL_TIMER(&os_timer_t);
ath_gpio_out_val(WPS_LED_GPIO, initial_led_state);
}
#endif /* ifdef WPS_LED_GPIO */
#ifdef JUMPSTART_GPIO
int ath_simple_config_invoke_cb(int simplecfg_only, int irq_enable, int cpl)
{
int i;
int cbnum = sizeof(sccallback)/sizeof(multi_callback_t);
printk("%s: sc %d, irq %d, ignorepb %d, jiffies %lu\n", __func__,
simplecfg_only, irq_enable, ignore_pushbutton, jiffies);
if (simplecfg_only) {
if (ignore_pushbutton) {
#ifndef CONFIG_MACH_QCA956x
ath_gpio_config_int(JUMPSTART_GPIO, INT_TYPE_LEVEL,
INT_POL_ACTIVE_HIGH);
#endif
ignore_pushbutton = 0;
push_time = jiffies;
return IRQ_HANDLED;
}
ath_gpio_config_int(JUMPSTART_GPIO, INT_TYPE_LEVEL,
INT_POL_ACTIVE_LOW);
ignore_pushbutton = 1;
}
if (irq_enable)
local_irq_enable();
if (push_time) {
#ifdef CONFIG_MACH_QCA956x
/* ignore continuous interrupt in short time */
u_int32_t diff_time;
if (push_time >= prev_push_time)
diff_time = push_time - prev_push_time;
else
diff_time = prev_push_time - push_time;
if (diff_time < 50)
return IRQ_HANDLED;
prev_push_time = push_time;
#endif
push_time = jiffies - push_time;
}
printk ("simple_config callback.. push dur in sec %d\n", push_time/HZ);
for (i = 0; i< cbnum; i++)
{
if (sccallback[i].registered_cb) {
if (sccallback[i].cb_arg2) {
*(u_int32_t *)sccallback[i].cb_arg2 = push_time/HZ;
}
sccallback[i].registered_cb (cpl, sccallback[i].cb_arg1, NULL, sccallback[i].cb_arg2);
}
}
return IRQ_HANDLED;
}
/*
* Irq for front panel SW jumpstart switch
* Connected to XSCALE through GPIO4
*/
irqreturn_t jumpstart_irq(int cpl, void *dev_id)
{
#ifndef AP_RESET_GPIO
unsigned int delay;
if (atomic_read(&ath_fr_status)) {
local_irq_disable();
#define UDELAY_COUNT 4000
push_time = jiffies;
for (delay = UDELAY_COUNT; delay; delay--) {
if (ath_gpio_in_val(JUMPSTART_GPIO)) {
break;
}
udelay(1000);
}
if (!delay) {
atomic_dec(&ath_fr_status);
/*
* since we are going to reboot the board, we
* don't need the interrupt handler anymore,
* so disable it.
*/
disable_irq(ATH_GPIO_IRQn(JUMPSTART_GPIO));
wake_up(&ath_fr_wq);
printk("\nath: factory configuration restored..\n");
local_irq_enable();
return IRQ_HANDLED;
} else {
return (ath_simple_config_invoke_cb
(0, 1, cpl));
}
} else
#endif
return (ath_simple_config_invoke_cb(1, 0, cpl));
}
#endif /* ifdef JUMPSTART_GPIO */
#ifdef AP_RESET_GPIO
irqreturn_t ath_reset_irq(int cpl, void *dev_id)
{
local_irq_disable();
if(push_time == 0){
ath_gpio_config_int(AP_RESET_GPIO, INT_TYPE_LEVEL,
INT_POL_ACTIVE_HIGH);
push_time = jiffies;
local_irq_enable();
return IRQ_HANDLED;
}else{
ath_gpio_config_int(AP_RESET_GPIO, INT_TYPE_LEVEL,
INT_POL_ACTIVE_LOW);
push_time = jiffies - push_time;
}
if(push_time/HZ > 3){
/*
* since we are going to reboot the board, we
* don't need the interrupt handler anymore,
* so disable it.
*/
disable_irq(ATH_GPIO_IRQn(AP_RESET_GPIO));
wake_up(&ath_fr_wq);
printk("\nath: factory configuration restored..\n");
push_time = 0;
local_irq_enable();
return IRQ_HANDLED;
} else if(push_time/HZ < 1){
push_time = 0;
local_irq_enable();
return IRQ_HANDLED;
}else{
extern void ath_restart(char *);
ath_restart(NULL);
return IRQ_HANDLED;
}
}
#endif
static int push_button_read(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
return 0;
}
static int push_button_write(struct file *file, const char *buf,
unsigned long count, void *data)
{
int i;
int cbnum = sizeof(sccallback)/sizeof(multi_callback_t);
for (i = 0; i< cbnum; i++){
if (sccallback[i].registered_cb) {
sccallback[i].registered_cb (0, sccallback[i].cb_arg1, 0, sccallback[i].cb_arg2);
}
}
return count;
}
typedef enum {
LED_STATE_OFF = 1,
LED_STATE_ON = 2,
LED_STATE_BLINKING = 3,
} led_state_e;
#ifdef WPS_LED_GPIO
static led_state_e simple_config_led_state = LED_STATE_OFF;
static int gpio_simple_config_led_read(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
return sprintf(page, "%d\n", simple_config_led_state);
}
static int gpio_simple_config_led_write(struct file *file, const char *buf,
unsigned long count, void *data)
{
u_int32_t val;
if (sscanf(buf, "%d", &val) != 1)
return -EINVAL;
if(val == SIMPLE_CONFIG_BLINK){
if( ath_gpio_in_val(WPS_LED_GPIO) == 0 ){
initial_led_state = WPS_LED_ON;
}else{
initial_led_state = WPS_LED_OFF;
}
}
if ((val == SIMPLE_CONFIG_BLINK) && !wps_led_blinking) { /* wps LED blinking */
wps_led_blinking = 1;
simple_config_led_state = SIMPLE_CONFIG_BLINK;
ath_gpio_out_val(WPS_LED_GPIO, WPS_LED_ON);
OS_CANCEL_TIMER(&os_timer_t);
OS_INIT_TIMER(NULL, &os_timer_t, wps_led_blink, &os_timer_t);
OS_SET_TIMER(&os_timer_t, 1000);
} else if (val == SIMPLE_CONFIG_FAIL) { /* WPS failed */
wps_led_blinking = 0;
simple_config_led_state = SIMPLE_CONFIG_FAIL;
ath_gpio_out_val(WPS_LED_GPIO, WPS_LED_ON);
OS_CANCEL_TIMER(&os_timer_t);
OS_INIT_TIMER(NULL, &os_timer_t, wps_led_fail, &os_timer_t);
OS_SET_TIMER(&os_timer_t, 200);
} else if (val == SIMPLE_CONFIG_ON) { /* WPS Success */
wps_led_blinking = 0;
simple_config_led_state = SIMPLE_CONFIG_ON;
OS_CANCEL_TIMER(&os_timer_t);
ath_gpio_out_val(WPS_LED_GPIO, WPS_LED_ON);
OS_INIT_TIMER(NULL, &os_timer_t, wps_led_success, &os_timer_t);
OS_SET_TIMER(&os_timer_t, 120000);
} else if (val == SIMPLE_CONFIG_OFF) { /* wps LED off */
wps_led_blinking = 0;
simple_config_led_state = SIMPLE_CONFIG_OFF;
OS_CANCEL_TIMER(&os_timer_t);
ath_gpio_out_val(WPS_LED_GPIO, initial_led_state);
}
return count;
}
#endif /* ifdef WPS_LED_GPIO */
void ap_usb_led_on(void)
{
#ifdef CONFIG_MACH_AR934x
#if !defined(CONFIG_I2S) && defined(AP_USB_LED_GPIO)
unsigned int rddata;
if (AP_USB_LED_GPIO == 4) {
rddata = ath_reg_rd(ATH_GPIO_OUT_FUNCTION1); //87- for USB suspend
rddata = rddata & 0xffffff00;
rddata = rddata | ATH_GPIO_OUT_FUNCTION1_ENABLE_GPIO_4(0x0);
ath_reg_wr(ATH_GPIO_OUT_FUNCTION1, rddata);
} else if (AP_USB_LED_GPIO == 11) {
rddata = ath_reg_rd(ATH_GPIO_OUT_FUNCTION2); //87- for USB suspend
rddata = rddata & 0x00ffffff;
rddata = rddata | ATH_GPIO_OUT_FUNCTION2_ENABLE_GPIO_11(0x0);
ath_reg_wr(ATH_GPIO_OUT_FUNCTION2, rddata);
}
ath_reg_rmw_clear(ATH_GPIO_OE, (1<<AP_USB_LED_GPIO));
ath_reg_rmw_clear(ATH_GPIO_OUT, (1<<AP_USB_LED_GPIO));
#endif
#else
# ifdef AP_USB_LED_GPIO
ath_gpio_config_output(AP_USB_LED_GPIO);
ath_gpio_set_fn(AP_USB_LED_GPIO, 0);
ath_gpio_out_val(AP_USB_LED_GPIO, USB_LED_ON);
# endif
#endif
}
EXPORT_SYMBOL(ap_usb_led_on);
void ap_usb_led_off(void)
{
#ifdef CONFIG_MACH_AR934x
#if !defined(CONFIG_I2S) && defined(AP_USB_LED_GPIO)
ath_reg_rmw_set(ATH_GPIO_OUT, (1<<AP_USB_LED_GPIO));
#endif
#else
# ifdef AP_USB_LED_GPIO
ath_gpio_out_val(AP_USB_LED_GPIO, USB_LED_OFF);
# endif
#endif
}
EXPORT_SYMBOL(ap_usb_led_off);
void ap_usb_host_led_on(int gpio)
{
#if defined(AP_USB1_LED_GPIO) || defined(AP_USB2_LED_GPIO)
ath_gpio_config_output(gpio);
ath_gpio_set_fn(gpio, 0);
ath_gpio_out_val(gpio, USB_LED_ON);
#endif
}
EXPORT_SYMBOL(ap_usb_host_led_on);
void ap_usb_host_led_off(int gpio)
{
#if defined(AP_USB1_LED_GPIO) || defined(AP_USB2_LED_GPIO)
ath_gpio_out_val(gpio, USB_LED_OFF);
#endif
}
EXPORT_SYMBOL(ap_usb_host_led_off);
void ath_config_slave_mdio_gpios(void)
{
unsigned int mask;
/* Slave MDIO phy address setting */
ath_reg_wr(MDIO_PHY_ADDR_ADDRESS, ATH_MDIO_PHY_ADDR);
/* Set the Clock Divisor */
mask = ath_reg_rd(ATH_MDIO_MAC_MII_MGMT_CFG) & ~(0xf);
mask |= ATH_MDIO_MGMT_CFG_CLK_DIV_28;
ath_reg_wr(ATH_MDIO_MAC_MII_MGMT_CFG, mask);
#ifndef CONFIG_MACH_QCA956x
/* Set External MDIO Multiplexing Register */
mask = ath_reg_rd(ATH_GPIO_IN_ENABLE3) &
~(GPIO_IN_ENABLE3_BOOT_EXT_MDC_MASK |
GPIO_IN_ENABLE3_BOOT_EXT_MDO_MASK);
mask |= GPIO_IN_ENABLE3_BOOT_EXT_MDC_SET(ATH_MDC_GPIO_PIN) |
GPIO_IN_ENABLE3_BOOT_EXT_MDO_SET(ATH_MDIO_GPIO_PIN);
ath_reg_wr(ATH_GPIO_IN_ENABLE3, mask);
#endif
/* Configure the Direction of GPIO Pins */
ath_gpio_config_input(ATH_MDC_GPIO_PIN);
ath_gpio_config_output(ATH_MDIO_GPIO_PIN);
/* Configure GPIO Output function as GPIOs */
ath_gpio_set_fn(ATH_MDIO_GPIO_PIN, ATH_MDIO_OUPUT_FUNC);
}
EXPORT_SYMBOL(ath_config_slave_mdio_gpios);
#ifdef POWER_ON_RLED_GPIO
static int power_on_finish_read(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
return sprintf(page, "%d\n", power_on_finish);
}
static int power_on_finish_write(struct file *file, const char *buf,
unsigned long count, void *data)
{
u_int32_t val;
if (sscanf(buf, "%d", &val) != 1)
return -EINVAL;
power_on_finish = 1;
return count;
}
static OS_TIMER_FUNC(power_led_blink)
{
static int power_led_status = POWER_LED_OFF, power_on_timeout = 0;
OS_CANCEL_TIMER(&power_on_timer);
if (power_on_finish) {
ath_gpio_out_val(POWER_ON_GLED_GPIO, POWER_LED_ON);
} else if (++power_on_timeout >= POWER_ON_TIMEOUT) {
ath_gpio_out_val(POWER_ON_GLED_GPIO, POWER_LED_OFF);
ath_gpio_config_input(POWER_ON_GLED_GPIO);
ath_gpio_config_output(POWER_ON_RLED_GPIO);
ath_gpio_out_val(POWER_ON_RLED_GPIO, POWER_LED_ON);
} else {
ath_gpio_out_val(POWER_ON_GLED_GPIO, power_led_status);
power_led_status = !power_led_status;
OS_SET_TIMER(&power_on_timer, POWER_LED_BLINK_INTERVAL);
}
}
#endif
#ifdef WPS_LED_GPIO
static int create_simple_config_led_proc_entry(void)
{
if (simple_config_entry != NULL) {
printk("Already have a proc entry for /proc/simple_config!\n");
return -ENOENT;
}
simple_config_entry = proc_mkdir("simple_config", NULL);
if (!simple_config_entry)
return -ENOENT;
simulate_push_button_entry = create_proc_entry("push_button", 0644,
simple_config_entry);
if (!simulate_push_button_entry)
return -ENOENT;
simulate_push_button_entry->write_proc = push_button_write;
simulate_push_button_entry->read_proc = push_button_read;
simple_config_led_entry = create_proc_entry("simple_config_led", 0644,
simple_config_entry);
if (!simple_config_led_entry)
return -ENOENT;
simple_config_led_entry->write_proc = gpio_simple_config_led_write;
simple_config_led_entry->read_proc = gpio_simple_config_led_read;
/* configure gpio as outputs */
ath_gpio_config_output(WPS_LED_GPIO);
/* switch off the led */
ath_gpio_out_val(WPS_LED_GPIO, WPS_LED_OFF);
#ifdef POWER_ON_RLED_GPIO
power_on_proc_entry = create_proc_entry("power_on_finish", 0644,
simple_config_entry);
if (!power_on_proc_entry)
return -ENOENT;
power_on_proc_entry->write_proc = power_on_finish_write;
power_on_proc_entry->read_proc = power_on_finish_read;
#endif
return 0;
}
#endif /* ifdef WPS_LED_GPIO */
static int
athfr_open(struct inode *inode, struct file *file)
{
if (MINOR(inode->i_rdev) != FACTORY_RESET_MINOR) {
return -ENODEV;
}
if (ath_fr_opened) {
return -EBUSY;
}
ath_fr_opened = 1;
return nonseekable_open(inode, file);
}
static int
athfr_close(struct inode *inode, struct file *file)
{
if (MINOR(inode->i_rdev) != FACTORY_RESET_MINOR) {
return -ENODEV;
}
ath_fr_opened = 0;
return 0;
}
static ssize_t
athfr_read(struct file *file, char *buf, size_t count, loff_t * ppos)
{
return -ENOTSUPP;
}
static ssize_t
athfr_write(struct file *file, const char *buf, size_t count, loff_t * ppos)
{
return -ENOTSUPP;
}
static int
athfr_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
switch (cmd) {
case ATH_FACTORY_RESET:
#ifndef AP_RESET_GPIO
atomic_inc(&ath_fr_status);
#endif
sleep_on(&ath_fr_wq);
break;
default:
ret = -EINVAL;
}
return ret;
}
static struct file_operations athfr_fops = {
read: athfr_read,
write: athfr_write,
ioctl: athfr_ioctl,
open: athfr_open,
release:athfr_close
};
static struct miscdevice athfr_miscdev = {
FACTORY_RESET_MINOR,
"Factory reset",
&athfr_fops
};
int __init ath_simple_config_init(void)
{
#ifdef CONFIG_CUS100
u32 mask = 0;
#endif
#ifdef JUMPSTART_GPIO
int req;
#endif
int ret;
#ifdef AP_RESET_GPIO
int req2;
#endif
ret = misc_register(&athfr_miscdev);
if (ret < 0) {
printk("*** ath misc_register failed %d *** \n", ret);
return -1;
}
#ifdef AP_RESET_GPIO
ath_gpio_config_input(AP_RESET_GPIO);
ath_gpio_config_int(AP_RESET_GPIO, INT_TYPE_LEVEL, INT_POL_ACTIVE_LOW);
printk("%s (%s) AP_RESET_GPIO: %d\n", __FILE__, __func__, AP_RESET_GPIO);
#endif
#ifdef JUMPSTART_GPIO
#ifdef CONFIG_CUS100
mask = ath_reg_rd(ATH_MISC_INT_MASK);
ath_reg_wr(ATH_MISC_INT_MASK, mask | (1 << 2));
ath_gpio_config_int(JUMPSTART_GPIO, INT_TYPE_LEVEL,
INT_POL_ACTIVE_HIGH);
ath_gpio_intr_enable(JUMPSTART_GPIO);
ath_gpio_config_input(JUMPSTART_GPIO);
#else
ath_gpio_config_input(JUMPSTART_GPIO);
/* configure Jumpstart GPIO as level triggered interrupt */
ath_gpio_config_int(JUMPSTART_GPIO, INT_TYPE_LEVEL,
INT_POL_ACTIVE_LOW);
printk("%s (%s) JUMPSTART_GPIO: %d\n", __FILE__, __func__,
JUMPSTART_GPIO);
#ifndef CONFIG_MACH_AR934x
ath_reg_rmw_clear(ATH_GPIO_FUNCTIONS, (1 << 2));
ath_reg_rmw_clear(ATH_GPIO_FUNCTIONS, (1 << 16));
ath_reg_rmw_clear(ATH_GPIO_FUNCTIONS, (1 << 20));
#endif
#endif
req = request_irq(ATH_GPIO_IRQn(JUMPSTART_GPIO), jumpstart_irq, 0,
#ifdef AP_RESET_GPIO
"SW JUMPSTART", NULL);
#else
"SW JUMPSTART/FACTORY RESET", NULL);
#endif
if (req != 0) {
printk("request_irq for jumpstart failed (error %d)\n", req);
misc_deregister(&athfr_miscdev);
ath_gpio_intr_shutdown(ATH_GPIO_IRQn(JUMPSTART_GPIO));
return -1;
}
#endif /* #ifdef JUMPSTART_GPIO */
#ifdef AP_RESET_GPIO
req2 = request_irq(ATH_GPIO_IRQn(AP_RESET_GPIO), ath_reset_irq, 0,
"FACTORY RESET", NULL);
if (req2 != 0) {
printk("request_irq for factory reset failed (error %d)\n", req);
misc_deregister(&athfr_miscdev);
free_irq(req, NULL);
return -1;
}
#endif
#ifdef ATH_S17INT_GPIO
ath_gpio_config_input(ATH_S17INT_GPIO);
/* configure S17 interrupt GPIO as level triggered interrupt */
ath_gpio_config_int(ATH_S17INT_GPIO, INT_TYPE_LEVEL,
INT_POL_ACTIVE_LOW);
printk("%s (%s) ATH_S17INT_GPIO: %d\n", __FILE__, __func__,
ATH_S17INT_GPIO);
#endif
#if !defined(CONFIG_I2S) && defined(AP_USB_LED_GPIO)
ath_gpio_config_output(AP_USB_LED_GPIO);
#endif
init_waitqueue_head(&ath_fr_wq);
#ifdef WPS_LED_GPIO
create_simple_config_led_proc_entry();
#endif
#ifdef POWER_ON_GLED_GPIO
printk("%s (%s) POWER_ON_GLED_GPIO: %d\n", __FILE__, __func__, POWER_ON_GLED_GPIO);
ath_gpio_config_output(POWER_ON_GLED_GPIO);
ath_gpio_out_val(POWER_ON_GLED_GPIO, POWER_LED_ON);
#endif
#ifdef POWER_ON_RLED_GPIO
printk("%s (%s) POWER_ON_RLED_GPIO: %d\n", __FILE__, __func__, POWER_ON_RLED_GPIO);
ath_gpio_config_output(POWER_ON_RLED_GPIO);
ath_gpio_out_val(POWER_ON_RLED_GPIO, POWER_LED_OFF);
OS_INIT_TIMER(NULL, &power_on_timer, power_led_blink, NULL);
OS_SET_TIMER(&power_on_timer, POWER_LED_BLINK_INTERVAL);
#endif
return 0;
}
/*
* used late_initcall so that misc_register will succeed
* otherwise, misc driver won't be in a initializated state
* thereby resulting in misc_register api to fail.
*/
#if !defined(CONFIG_ATH_EMULATION)
late_initcall(ath_simple_config_init);
#endif