Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 260303
b: refs/heads/master
c: 2cf2bae
h: refs/heads/master
i:
  260301: 8a650e1
  260299: 42b4c20
  260295: 0be00dd
  260287: a583c02
v: v3
  • Loading branch information
Daniel Drake authored and H. Peter Anvin committed Jul 6, 2011
1 parent adfd1d5 commit 5c0eb5d
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 2 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 7bc74b3df73776fe06f3df9fafd2d2698e6ca28a
refs/heads/master: 2cf2baea103f0a3d68b0f989d28df66f16dbc834
1 change: 1 addition & 0 deletions trunk/arch/x86/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -2090,6 +2090,7 @@ config OLPC_XO1_SCI
- EC-driven system wakeups
- Power button
- Ebook switch
- Lid switch

endif # X86_32

Expand Down
207 changes: 206 additions & 1 deletion trunk/arch/x86/platform/olpc/olpc-xo1-sci.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,26 @@
static unsigned long acpi_base;
static struct input_dev *power_button_idev;
static struct input_dev *ebook_switch_idev;
static struct input_dev *lid_switch_idev;

static int sci_irq;

static bool lid_open;
static bool lid_inverted;
static int lid_wake_mode;

enum lid_wake_modes {
LID_WAKE_ALWAYS,
LID_WAKE_OPEN,
LID_WAKE_CLOSE,
};

static const char * const lid_wake_mode_names[] = {
[LID_WAKE_ALWAYS] = "always",
[LID_WAKE_OPEN] = "open",
[LID_WAKE_CLOSE] = "close",
};

/* Report current ebook switch state through input layer */
static void send_ebook_state(void)
{
Expand All @@ -49,6 +66,70 @@ static void send_ebook_state(void)
input_sync(ebook_switch_idev);
}

static void flip_lid_inverter(void)
{
/* gpio is high; invert so we'll get l->h event interrupt */
if (lid_inverted)
cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_INPUT_INVERT);
else
cs5535_gpio_set(OLPC_GPIO_LID, GPIO_INPUT_INVERT);
lid_inverted = !lid_inverted;
}

static void detect_lid_state(void)
{
/*
* the edge detector hookup on the gpio inputs on the geode is
* odd, to say the least. See http://dev.laptop.org/ticket/5703
* for details, but in a nutshell: we don't use the edge
* detectors. instead, we make use of an anomoly: with the both
* edge detectors turned off, we still get an edge event on a
* positive edge transition. to take advantage of this, we use the
* front-end inverter to ensure that that's the edge we're always
* going to see next.
*/

int state;

state = cs5535_gpio_isset(OLPC_GPIO_LID, GPIO_READ_BACK);
lid_open = !state ^ !lid_inverted; /* x ^^ y */
if (!state)
return;

flip_lid_inverter();
}

/* Report current lid switch state through input layer */
static void send_lid_state(void)
{
input_report_switch(lid_switch_idev, SW_LID, !lid_open);
input_sync(lid_switch_idev);
}

static ssize_t lid_wake_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
const char *mode = lid_wake_mode_names[lid_wake_mode];
return sprintf(buf, "%s\n", mode);
}
static ssize_t lid_wake_mode_set(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int i;
for (i = 0; i < ARRAY_SIZE(lid_wake_mode_names); i++) {
const char *mode = lid_wake_mode_names[i];
if (strlen(mode) != count || strncasecmp(mode, buf, count))
continue;

lid_wake_mode = i;
return count;
}
return -EINVAL;
}
static DEVICE_ATTR(lid_wake_mode, S_IWUSR | S_IRUGO, lid_wake_mode_show,
lid_wake_mode_set);

/*
* Process all items in the EC's SCI queue.
*
Expand Down Expand Up @@ -111,6 +192,11 @@ static irqreturn_t xo1_sci_intr(int irq, void *dev_id)
schedule_work(&sci_work);
}

cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS);
cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS);
detect_lid_state();
send_lid_state();

return IRQ_HANDLED;
}

Expand All @@ -126,11 +212,32 @@ static int xo1_sci_suspend(struct platform_device *pdev, pm_message_t state)
else
olpc_ec_wakeup_clear(EC_SCI_SRC_EBOOK);

if (!device_may_wakeup(&lid_switch_idev->dev)) {
cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
} else if ((lid_open && lid_wake_mode == LID_WAKE_OPEN) ||
(!lid_open && lid_wake_mode == LID_WAKE_CLOSE)) {
flip_lid_inverter();

/* we may have just caused an event */
cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS);
cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS);

cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
}

return 0;
}

static int xo1_sci_resume(struct platform_device *pdev)
{
/*
* We don't know what may have happened while we were asleep.
* Reestablish our lid setup so we're sure to catch all transitions.
*/
detect_lid_state();
send_lid_state();
cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);

/* Enable all EC events */
olpc_ec_mask_write(EC_SCI_SRC_ALL);
return 0;
Expand Down Expand Up @@ -221,6 +328,43 @@ static void free_ec_sci(void)
gpio_free(OLPC_GPIO_ECSCI);
}

static int __devinit setup_lid_events(void)
{
int r;

r = gpio_request(OLPC_GPIO_LID, "OLPC-LID");
if (r)
return r;

gpio_direction_input(OLPC_GPIO_LID);

cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_INPUT_INVERT);
lid_inverted = 0;

/* Clear edge detection and event enable for now */
cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);
cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_EN);
cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_EN);
cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS);
cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS);

/* Set the LID to cause an PME event on group 6 */
cs5535_gpio_setup_event(OLPC_GPIO_LID, 6, 1);

/* Set PME group 6 to fire the SCI interrupt */
cs5535_gpio_set_irq(6, sci_irq);

/* Enable the event */
cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE);

return 0;
}

static void free_lid_events(void)
{
gpio_free(OLPC_GPIO_LID);
}

static int __devinit setup_power_button(struct platform_device *pdev)
{
int r;
Expand Down Expand Up @@ -283,6 +427,50 @@ static void free_ebook_switch(void)
input_free_device(ebook_switch_idev);
}

static int __devinit setup_lid_switch(struct platform_device *pdev)
{
int r;

lid_switch_idev = input_allocate_device();
if (!lid_switch_idev)
return -ENOMEM;

lid_switch_idev->name = "Lid Switch";
lid_switch_idev->phys = DRV_NAME "/input2";
set_bit(EV_SW, lid_switch_idev->evbit);
set_bit(SW_LID, lid_switch_idev->swbit);

lid_switch_idev->dev.parent = &pdev->dev;
device_set_wakeup_capable(&lid_switch_idev->dev, true);

r = input_register_device(lid_switch_idev);
if (r) {
dev_err(&pdev->dev, "failed to register lid switch: %d\n", r);
goto err_register;
}

r = device_create_file(&lid_switch_idev->dev, &dev_attr_lid_wake_mode);
if (r) {
dev_err(&pdev->dev, "failed to create wake mode attr: %d\n", r);
goto err_create_attr;
}

return 0;

err_create_attr:
input_unregister_device(lid_switch_idev);
err_register:
input_free_device(lid_switch_idev);
return r;
}

static void free_lid_switch(void)
{
device_remove_file(&lid_switch_idev->dev, &dev_attr_lid_wake_mode);
input_unregister_device(lid_switch_idev);
input_free_device(lid_switch_idev);
}

static int __devinit xo1_sci_probe(struct platform_device *pdev)
{
struct resource *res;
Expand Down Expand Up @@ -311,19 +499,30 @@ static int __devinit xo1_sci_probe(struct platform_device *pdev)
if (r)
goto err_ebook;

r = setup_lid_switch(pdev);
if (r)
goto err_lid;

r = setup_lid_events();
if (r)
goto err_lidevt;

r = setup_ec_sci();
if (r)
goto err_ecsci;

/* Enable PME generation for EC-generated events */
outl(CS5536_GPIOM7_PME_EN, acpi_base + CS5536_PM_GPE0_EN);
outl(CS5536_GPIOM6_PME_EN | CS5536_GPIOM7_PME_EN,
acpi_base + CS5536_PM_GPE0_EN);

/* Clear pending events */
outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
process_sci_queue(false);

/* Initial sync */
send_ebook_state();
detect_lid_state();
send_lid_state();

r = setup_sci_interrupt(pdev);
if (r)
Expand All @@ -337,6 +536,10 @@ static int __devinit xo1_sci_probe(struct platform_device *pdev)
err_sci:
free_ec_sci();
err_ecsci:
free_lid_events();
err_lidevt:
free_lid_switch();
err_lid:
free_ebook_switch();
err_ebook:
free_power_button();
Expand All @@ -349,6 +552,8 @@ static int __devexit xo1_sci_remove(struct platform_device *pdev)
free_irq(sci_irq, pdev);
cancel_work_sync(&sci_work);
free_ec_sci();
free_lid_events();
free_lid_switch();
free_ebook_switch();
free_power_button();
acpi_base = 0;
Expand Down
1 change: 1 addition & 0 deletions trunk/include/linux/cs5535.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ static inline int cs5535_pic_unreqz_select_high(unsigned int group,

/* CS5536_PM_GPE0_EN bits */
#define CS5536_GPIOM7_PME_EN (1 << 31)
#define CS5536_GPIOM6_PME_EN (1 << 30)

/* VSA2 magic values */
#define VSA_VRC_INDEX 0xAC1C
Expand Down

0 comments on commit 5c0eb5d

Please sign in to comment.