Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 204691
b: refs/heads/master
c: 8fe93f8
h: refs/heads/master
i:
  204689: a6e9f20
  204687: 150712a
v: v3
  • Loading branch information
Brian King authored and Benjamin Herrenschmidt committed Jul 9, 2010
1 parent 0093595 commit 82da6fe
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 36 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: c1aa687d499a8bce55cb8cf962f0b72c0f933f14
refs/heads/master: 8fe93f8d850a24581e9d47df5814b257fe451052
1 change: 1 addition & 0 deletions trunk/arch/powerpc/include/asm/hvcall.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
#define H_NOT_ENOUGH_RESOURCES -44
#define H_R_STATE -45
#define H_RESCINDEND -46
#define H_MULTI_THREADS_ACTIVE -9005


/* Long Busy is a condition that can be returned by the firmware
Expand Down
10 changes: 10 additions & 0 deletions trunk/arch/powerpc/include/asm/rtas.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ struct rtas_t {
struct device_node *dev; /* virtual address pointer */
};

struct rtas_suspend_me_data {
atomic_t working; /* number of cpus accessing this struct */
atomic_t done;
int token; /* ibm,suspend-me */
atomic_t error;
struct completion *complete; /* wait on this until working == 0 */
};

/* RTAS event classes */
#define RTAS_INTERNAL_ERROR 0x80000000 /* set bit 0 */
#define RTAS_EPOW_WARNING 0x40000000 /* set bit 1 */
Expand Down Expand Up @@ -174,6 +182,8 @@ extern int rtas_set_indicator(int indicator, int index, int new_value);
extern int rtas_set_indicator_fast(int indicator, int index, int new_value);
extern void rtas_progress(char *s, unsigned short hex);
extern void rtas_initialize(void);
extern int rtas_suspend_cpu(struct rtas_suspend_me_data *data);
extern int rtas_suspend_last_cpu(struct rtas_suspend_me_data *data);

struct rtc_time;
extern unsigned long rtas_get_boot_time(void);
Expand Down
105 changes: 70 additions & 35 deletions trunk/arch/powerpc/kernel/rtas.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,6 @@ struct rtas_t rtas = {
};
EXPORT_SYMBOL(rtas);

struct rtas_suspend_me_data {
atomic_t working; /* number of cpus accessing this struct */
atomic_t done;
int token; /* ibm,suspend-me */
int error;
struct completion *complete; /* wait on this until working == 0 */
};

DEFINE_SPINLOCK(rtas_data_buf_lock);
EXPORT_SYMBOL(rtas_data_buf_lock);

Expand Down Expand Up @@ -714,22 +706,61 @@ void rtas_os_term(char *str)

static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE;
#ifdef CONFIG_PPC_PSERIES
static void rtas_percpu_suspend_me(void *info)
static int __rtas_suspend_last_cpu(struct rtas_suspend_me_data *data, int wake_when_done)
{
u16 slb_size = mmu_slb_size;
int rc = H_MULTI_THREADS_ACTIVE;
int cpu;

slb_set_size(SLB_MIN_SIZE);
printk(KERN_DEBUG "calling ibm,suspend-me on cpu %i\n", smp_processor_id());

while (rc == H_MULTI_THREADS_ACTIVE && !atomic_read(&data->done) &&
!atomic_read(&data->error))
rc = rtas_call(data->token, 0, 1, NULL);

if (rc || atomic_read(&data->error)) {
printk(KERN_DEBUG "ibm,suspend-me returned %d\n", rc);
slb_set_size(slb_size);
}

if (atomic_read(&data->error))
rc = atomic_read(&data->error);

atomic_set(&data->error, rc);

if (wake_when_done) {
atomic_set(&data->done, 1);

for_each_online_cpu(cpu)
plpar_hcall_norets(H_PROD, get_hard_smp_processor_id(cpu));
}

if (atomic_dec_return(&data->working) == 0)
complete(data->complete);

return rc;
}

int rtas_suspend_last_cpu(struct rtas_suspend_me_data *data)
{
atomic_inc(&data->working);
return __rtas_suspend_last_cpu(data, 0);
}

static int __rtas_suspend_cpu(struct rtas_suspend_me_data *data, int wake_when_done)
{
long rc = H_SUCCESS;
unsigned long msr_save;
u16 slb_size = mmu_slb_size;
int cpu;
struct rtas_suspend_me_data *data =
(struct rtas_suspend_me_data *)info;

atomic_inc(&data->working);

/* really need to ensure MSR.EE is off for H_JOIN */
msr_save = mfmsr();
mtmsr(msr_save & ~(MSR_EE));

while (rc == H_SUCCESS && !atomic_read(&data->done))
while (rc == H_SUCCESS && !atomic_read(&data->done) && !atomic_read(&data->error))
rc = plpar_hcall_norets(H_JOIN);

mtmsr(msr_save);
Expand All @@ -741,33 +772,37 @@ static void rtas_percpu_suspend_me(void *info)
/* All other cpus are in H_JOIN, this cpu does
* the suspend.
*/
slb_set_size(SLB_MIN_SIZE);
printk(KERN_DEBUG "calling ibm,suspend-me on cpu %i\n",
smp_processor_id());
data->error = rtas_call(data->token, 0, 1, NULL);

if (data->error) {
printk(KERN_DEBUG "ibm,suspend-me returned %d\n",
data->error);
slb_set_size(slb_size);
}
return __rtas_suspend_last_cpu(data, wake_when_done);
} else {
printk(KERN_ERR "H_JOIN on cpu %i failed with rc = %ld\n",
smp_processor_id(), rc);
data->error = rc;
atomic_set(&data->error, rc);
}

atomic_set(&data->done, 1);
if (wake_when_done) {
atomic_set(&data->done, 1);

/* This cpu did the suspend or got an error; in either case,
* we need to prod all other other cpus out of join state.
* Extra prods are harmless.
*/
for_each_online_cpu(cpu)
plpar_hcall_norets(H_PROD, get_hard_smp_processor_id(cpu));
/* This cpu did the suspend or got an error; in either case,
* we need to prod all other other cpus out of join state.
* Extra prods are harmless.
*/
for_each_online_cpu(cpu)
plpar_hcall_norets(H_PROD, get_hard_smp_processor_id(cpu));
}
out:
if (atomic_dec_return(&data->working) == 0)
complete(data->complete);
return rc;
}

int rtas_suspend_cpu(struct rtas_suspend_me_data *data)
{
return __rtas_suspend_cpu(data, 0);
}

static void rtas_percpu_suspend_me(void *info)
{
__rtas_suspend_cpu((struct rtas_suspend_me_data *)info, 1);
}

static int rtas_ibm_suspend_me(struct rtas_args *args)
Expand Down Expand Up @@ -802,22 +837,22 @@ static int rtas_ibm_suspend_me(struct rtas_args *args)

atomic_set(&data.working, 0);
atomic_set(&data.done, 0);
atomic_set(&data.error, 0);
data.token = rtas_token("ibm,suspend-me");
data.error = 0;
data.complete = &done;

/* Call function on all CPUs. One of us will make the
* rtas call
*/
if (on_each_cpu(rtas_percpu_suspend_me, &data, 0))
data.error = -EINVAL;
atomic_set(&data.error, -EINVAL);

wait_for_completion(&done);

if (data.error != 0)
if (atomic_read(&data.error) != 0)
printk(KERN_ERR "Error doing global join\n");

return data.error;
return atomic_read(&data.error);
}
#else /* CONFIG_PPC_PSERIES */
static int rtas_ibm_suspend_me(struct rtas_args *args)
Expand Down

0 comments on commit 82da6fe

Please sign in to comment.