162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* ds.c: Domain Services driver for Logical Domains
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci#include <linux/string.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/sched.h>
1362306a36Sopenharmony_ci#include <linux/sched/clock.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/mutex.h>
1662306a36Sopenharmony_ci#include <linux/kthread.h>
1762306a36Sopenharmony_ci#include <linux/reboot.h>
1862306a36Sopenharmony_ci#include <linux/cpu.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <asm/hypervisor.h>
2162306a36Sopenharmony_ci#include <asm/ldc.h>
2262306a36Sopenharmony_ci#include <asm/vio.h>
2362306a36Sopenharmony_ci#include <asm/mdesc.h>
2462306a36Sopenharmony_ci#include <asm/head.h>
2562306a36Sopenharmony_ci#include <asm/irq.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "kernel.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define DRV_MODULE_NAME		"ds"
3062306a36Sopenharmony_ci#define PFX DRV_MODULE_NAME	": "
3162306a36Sopenharmony_ci#define DRV_MODULE_VERSION	"1.0"
3262306a36Sopenharmony_ci#define DRV_MODULE_RELDATE	"Jul 11, 2007"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic char version[] =
3562306a36Sopenharmony_ci	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
3662306a36Sopenharmony_ciMODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
3762306a36Sopenharmony_ciMODULE_DESCRIPTION("Sun LDOM domain services driver");
3862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3962306a36Sopenharmony_ciMODULE_VERSION(DRV_MODULE_VERSION);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistruct ds_msg_tag {
4262306a36Sopenharmony_ci	__u32			type;
4362306a36Sopenharmony_ci#define DS_INIT_REQ		0x00
4462306a36Sopenharmony_ci#define DS_INIT_ACK		0x01
4562306a36Sopenharmony_ci#define DS_INIT_NACK		0x02
4662306a36Sopenharmony_ci#define DS_REG_REQ		0x03
4762306a36Sopenharmony_ci#define DS_REG_ACK		0x04
4862306a36Sopenharmony_ci#define DS_REG_NACK		0x05
4962306a36Sopenharmony_ci#define DS_UNREG_REQ		0x06
5062306a36Sopenharmony_ci#define DS_UNREG_ACK		0x07
5162306a36Sopenharmony_ci#define DS_UNREG_NACK		0x08
5262306a36Sopenharmony_ci#define DS_DATA			0x09
5362306a36Sopenharmony_ci#define DS_NACK			0x0a
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	__u32			len;
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* Result codes */
5962306a36Sopenharmony_ci#define DS_OK			0x00
6062306a36Sopenharmony_ci#define DS_REG_VER_NACK		0x01
6162306a36Sopenharmony_ci#define DS_REG_DUP		0x02
6262306a36Sopenharmony_ci#define DS_INV_HDL		0x03
6362306a36Sopenharmony_ci#define DS_TYPE_UNKNOWN		0x04
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistruct ds_version {
6662306a36Sopenharmony_ci	__u16			major;
6762306a36Sopenharmony_ci	__u16			minor;
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistruct ds_ver_req {
7162306a36Sopenharmony_ci	struct ds_msg_tag	tag;
7262306a36Sopenharmony_ci	struct ds_version	ver;
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistruct ds_ver_ack {
7662306a36Sopenharmony_ci	struct ds_msg_tag	tag;
7762306a36Sopenharmony_ci	__u16			minor;
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistruct ds_ver_nack {
8162306a36Sopenharmony_ci	struct ds_msg_tag	tag;
8262306a36Sopenharmony_ci	__u16			major;
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistruct ds_reg_req {
8662306a36Sopenharmony_ci	struct ds_msg_tag	tag;
8762306a36Sopenharmony_ci	__u64			handle;
8862306a36Sopenharmony_ci	__u16			major;
8962306a36Sopenharmony_ci	__u16			minor;
9062306a36Sopenharmony_ci	char			svc_id[];
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistruct ds_reg_ack {
9462306a36Sopenharmony_ci	struct ds_msg_tag	tag;
9562306a36Sopenharmony_ci	__u64			handle;
9662306a36Sopenharmony_ci	__u16			minor;
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistruct ds_reg_nack {
10062306a36Sopenharmony_ci	struct ds_msg_tag	tag;
10162306a36Sopenharmony_ci	__u64			handle;
10262306a36Sopenharmony_ci	__u16			major;
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistruct ds_unreg_req {
10662306a36Sopenharmony_ci	struct ds_msg_tag	tag;
10762306a36Sopenharmony_ci	__u64			handle;
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistruct ds_unreg_ack {
11162306a36Sopenharmony_ci	struct ds_msg_tag	tag;
11262306a36Sopenharmony_ci	__u64			handle;
11362306a36Sopenharmony_ci};
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistruct ds_unreg_nack {
11662306a36Sopenharmony_ci	struct ds_msg_tag	tag;
11762306a36Sopenharmony_ci	__u64			handle;
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistruct ds_data {
12162306a36Sopenharmony_ci	struct ds_msg_tag	tag;
12262306a36Sopenharmony_ci	__u64			handle;
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistruct ds_data_nack {
12662306a36Sopenharmony_ci	struct ds_msg_tag	tag;
12762306a36Sopenharmony_ci	__u64			handle;
12862306a36Sopenharmony_ci	__u64			result;
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistruct ds_info;
13262306a36Sopenharmony_cistruct ds_cap_state {
13362306a36Sopenharmony_ci	__u64			handle;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	void			(*data)(struct ds_info *dp,
13662306a36Sopenharmony_ci					struct ds_cap_state *cp,
13762306a36Sopenharmony_ci					void *buf, int len);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	const char		*service_id;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	u8			state;
14262306a36Sopenharmony_ci#define CAP_STATE_UNKNOWN	0x00
14362306a36Sopenharmony_ci#define CAP_STATE_REG_SENT	0x01
14462306a36Sopenharmony_ci#define CAP_STATE_REGISTERED	0x02
14562306a36Sopenharmony_ci};
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic void md_update_data(struct ds_info *dp, struct ds_cap_state *cp,
14862306a36Sopenharmony_ci			   void *buf, int len);
14962306a36Sopenharmony_cistatic void domain_shutdown_data(struct ds_info *dp,
15062306a36Sopenharmony_ci				 struct ds_cap_state *cp,
15162306a36Sopenharmony_ci				 void *buf, int len);
15262306a36Sopenharmony_cistatic void domain_panic_data(struct ds_info *dp,
15362306a36Sopenharmony_ci			      struct ds_cap_state *cp,
15462306a36Sopenharmony_ci			      void *buf, int len);
15562306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
15662306a36Sopenharmony_cistatic void dr_cpu_data(struct ds_info *dp,
15762306a36Sopenharmony_ci			struct ds_cap_state *cp,
15862306a36Sopenharmony_ci			void *buf, int len);
15962306a36Sopenharmony_ci#endif
16062306a36Sopenharmony_cistatic void ds_pri_data(struct ds_info *dp,
16162306a36Sopenharmony_ci			struct ds_cap_state *cp,
16262306a36Sopenharmony_ci			void *buf, int len);
16362306a36Sopenharmony_cistatic void ds_var_data(struct ds_info *dp,
16462306a36Sopenharmony_ci			struct ds_cap_state *cp,
16562306a36Sopenharmony_ci			void *buf, int len);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic struct ds_cap_state ds_states_template[] = {
16862306a36Sopenharmony_ci	{
16962306a36Sopenharmony_ci		.service_id	= "md-update",
17062306a36Sopenharmony_ci		.data		= md_update_data,
17162306a36Sopenharmony_ci	},
17262306a36Sopenharmony_ci	{
17362306a36Sopenharmony_ci		.service_id	= "domain-shutdown",
17462306a36Sopenharmony_ci		.data		= domain_shutdown_data,
17562306a36Sopenharmony_ci	},
17662306a36Sopenharmony_ci	{
17762306a36Sopenharmony_ci		.service_id	= "domain-panic",
17862306a36Sopenharmony_ci		.data		= domain_panic_data,
17962306a36Sopenharmony_ci	},
18062306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
18162306a36Sopenharmony_ci	{
18262306a36Sopenharmony_ci		.service_id	= "dr-cpu",
18362306a36Sopenharmony_ci		.data		= dr_cpu_data,
18462306a36Sopenharmony_ci	},
18562306a36Sopenharmony_ci#endif
18662306a36Sopenharmony_ci	{
18762306a36Sopenharmony_ci		.service_id	= "pri",
18862306a36Sopenharmony_ci		.data		= ds_pri_data,
18962306a36Sopenharmony_ci	},
19062306a36Sopenharmony_ci	{
19162306a36Sopenharmony_ci		.service_id	= "var-config",
19262306a36Sopenharmony_ci		.data		= ds_var_data,
19362306a36Sopenharmony_ci	},
19462306a36Sopenharmony_ci	{
19562306a36Sopenharmony_ci		.service_id	= "var-config-backup",
19662306a36Sopenharmony_ci		.data		= ds_var_data,
19762306a36Sopenharmony_ci	},
19862306a36Sopenharmony_ci};
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(ds_lock);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistruct ds_info {
20362306a36Sopenharmony_ci	struct ldc_channel	*lp;
20462306a36Sopenharmony_ci	u8			hs_state;
20562306a36Sopenharmony_ci#define DS_HS_START		0x01
20662306a36Sopenharmony_ci#define DS_HS_DONE		0x02
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	u64			id;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	void			*rcv_buf;
21162306a36Sopenharmony_ci	int			rcv_buf_len;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	struct ds_cap_state	*ds_states;
21462306a36Sopenharmony_ci	int			num_ds_states;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	struct ds_info		*next;
21762306a36Sopenharmony_ci};
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic struct ds_info *ds_info_list;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic struct ds_cap_state *find_cap(struct ds_info *dp, u64 handle)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	unsigned int index = handle >> 32;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (index >= dp->num_ds_states)
22662306a36Sopenharmony_ci		return NULL;
22762306a36Sopenharmony_ci	return &dp->ds_states[index];
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic struct ds_cap_state *find_cap_by_string(struct ds_info *dp,
23162306a36Sopenharmony_ci					       const char *name)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	int i;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	for (i = 0; i < dp->num_ds_states; i++) {
23662306a36Sopenharmony_ci		if (strcmp(dp->ds_states[i].service_id, name))
23762306a36Sopenharmony_ci			continue;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		return &dp->ds_states[i];
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci	return NULL;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic int __ds_send(struct ldc_channel *lp, void *data, int len)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	int err, limit = 1000;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	err = -EINVAL;
24962306a36Sopenharmony_ci	while (limit-- > 0) {
25062306a36Sopenharmony_ci		err = ldc_write(lp, data, len);
25162306a36Sopenharmony_ci		if (!err || (err != -EAGAIN))
25262306a36Sopenharmony_ci			break;
25362306a36Sopenharmony_ci		udelay(1);
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return err;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int ds_send(struct ldc_channel *lp, void *data, int len)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	unsigned long flags;
26262306a36Sopenharmony_ci	int err;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	spin_lock_irqsave(&ds_lock, flags);
26562306a36Sopenharmony_ci	err = __ds_send(lp, data, len);
26662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ds_lock, flags);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return err;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistruct ds_md_update_req {
27262306a36Sopenharmony_ci	__u64				req_num;
27362306a36Sopenharmony_ci};
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistruct ds_md_update_res {
27662306a36Sopenharmony_ci	__u64				req_num;
27762306a36Sopenharmony_ci	__u32				result;
27862306a36Sopenharmony_ci};
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic void md_update_data(struct ds_info *dp,
28162306a36Sopenharmony_ci			   struct ds_cap_state *cp,
28262306a36Sopenharmony_ci			   void *buf, int len)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	struct ldc_channel *lp = dp->lp;
28562306a36Sopenharmony_ci	struct ds_data *dpkt = buf;
28662306a36Sopenharmony_ci	struct ds_md_update_req *rp;
28762306a36Sopenharmony_ci	struct {
28862306a36Sopenharmony_ci		struct ds_data		data;
28962306a36Sopenharmony_ci		struct ds_md_update_res	res;
29062306a36Sopenharmony_ci	} pkt;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	rp = (struct ds_md_update_req *) (dpkt + 1);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	printk(KERN_INFO "ds-%llu: Machine description update.\n", dp->id);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	mdesc_update();
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	memset(&pkt, 0, sizeof(pkt));
29962306a36Sopenharmony_ci	pkt.data.tag.type = DS_DATA;
30062306a36Sopenharmony_ci	pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag);
30162306a36Sopenharmony_ci	pkt.data.handle = cp->handle;
30262306a36Sopenharmony_ci	pkt.res.req_num = rp->req_num;
30362306a36Sopenharmony_ci	pkt.res.result = DS_OK;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	ds_send(lp, &pkt, sizeof(pkt));
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistruct ds_shutdown_req {
30962306a36Sopenharmony_ci	__u64				req_num;
31062306a36Sopenharmony_ci	__u32				ms_delay;
31162306a36Sopenharmony_ci};
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistruct ds_shutdown_res {
31462306a36Sopenharmony_ci	__u64				req_num;
31562306a36Sopenharmony_ci	__u32				result;
31662306a36Sopenharmony_ci	char				reason[1];
31762306a36Sopenharmony_ci};
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic void domain_shutdown_data(struct ds_info *dp,
32062306a36Sopenharmony_ci				 struct ds_cap_state *cp,
32162306a36Sopenharmony_ci				 void *buf, int len)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct ldc_channel *lp = dp->lp;
32462306a36Sopenharmony_ci	struct ds_data *dpkt = buf;
32562306a36Sopenharmony_ci	struct ds_shutdown_req *rp;
32662306a36Sopenharmony_ci	struct {
32762306a36Sopenharmony_ci		struct ds_data		data;
32862306a36Sopenharmony_ci		struct ds_shutdown_res	res;
32962306a36Sopenharmony_ci	} pkt;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	rp = (struct ds_shutdown_req *) (dpkt + 1);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	printk(KERN_ALERT "ds-%llu: Shutdown request from "
33462306a36Sopenharmony_ci	       "LDOM manager received.\n", dp->id);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	memset(&pkt, 0, sizeof(pkt));
33762306a36Sopenharmony_ci	pkt.data.tag.type = DS_DATA;
33862306a36Sopenharmony_ci	pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag);
33962306a36Sopenharmony_ci	pkt.data.handle = cp->handle;
34062306a36Sopenharmony_ci	pkt.res.req_num = rp->req_num;
34162306a36Sopenharmony_ci	pkt.res.result = DS_OK;
34262306a36Sopenharmony_ci	pkt.res.reason[0] = 0;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	ds_send(lp, &pkt, sizeof(pkt));
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	orderly_poweroff(true);
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistruct ds_panic_req {
35062306a36Sopenharmony_ci	__u64				req_num;
35162306a36Sopenharmony_ci};
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistruct ds_panic_res {
35462306a36Sopenharmony_ci	__u64				req_num;
35562306a36Sopenharmony_ci	__u32				result;
35662306a36Sopenharmony_ci	char				reason[1];
35762306a36Sopenharmony_ci};
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistatic void domain_panic_data(struct ds_info *dp,
36062306a36Sopenharmony_ci			      struct ds_cap_state *cp,
36162306a36Sopenharmony_ci			      void *buf, int len)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct ldc_channel *lp = dp->lp;
36462306a36Sopenharmony_ci	struct ds_data *dpkt = buf;
36562306a36Sopenharmony_ci	struct ds_panic_req *rp;
36662306a36Sopenharmony_ci	struct {
36762306a36Sopenharmony_ci		struct ds_data		data;
36862306a36Sopenharmony_ci		struct ds_panic_res	res;
36962306a36Sopenharmony_ci	} pkt;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	rp = (struct ds_panic_req *) (dpkt + 1);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	printk(KERN_ALERT "ds-%llu: Panic request from "
37462306a36Sopenharmony_ci	       "LDOM manager received.\n", dp->id);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	memset(&pkt, 0, sizeof(pkt));
37762306a36Sopenharmony_ci	pkt.data.tag.type = DS_DATA;
37862306a36Sopenharmony_ci	pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag);
37962306a36Sopenharmony_ci	pkt.data.handle = cp->handle;
38062306a36Sopenharmony_ci	pkt.res.req_num = rp->req_num;
38162306a36Sopenharmony_ci	pkt.res.result = DS_OK;
38262306a36Sopenharmony_ci	pkt.res.reason[0] = 0;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	ds_send(lp, &pkt, sizeof(pkt));
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	panic("PANIC requested by LDOM manager.");
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
39062306a36Sopenharmony_cistruct dr_cpu_tag {
39162306a36Sopenharmony_ci	__u64				req_num;
39262306a36Sopenharmony_ci	__u32				type;
39362306a36Sopenharmony_ci#define DR_CPU_CONFIGURE		0x43
39462306a36Sopenharmony_ci#define DR_CPU_UNCONFIGURE		0x55
39562306a36Sopenharmony_ci#define DR_CPU_FORCE_UNCONFIGURE	0x46
39662306a36Sopenharmony_ci#define DR_CPU_STATUS			0x53
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci/* Responses */
39962306a36Sopenharmony_ci#define DR_CPU_OK			0x6f
40062306a36Sopenharmony_ci#define DR_CPU_ERROR			0x65
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	__u32				num_records;
40362306a36Sopenharmony_ci};
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistruct dr_cpu_resp_entry {
40662306a36Sopenharmony_ci	__u32				cpu;
40762306a36Sopenharmony_ci	__u32				result;
40862306a36Sopenharmony_ci#define DR_CPU_RES_OK			0x00
40962306a36Sopenharmony_ci#define DR_CPU_RES_FAILURE		0x01
41062306a36Sopenharmony_ci#define DR_CPU_RES_BLOCKED		0x02
41162306a36Sopenharmony_ci#define DR_CPU_RES_CPU_NOT_RESPONDING	0x03
41262306a36Sopenharmony_ci#define DR_CPU_RES_NOT_IN_MD		0x04
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	__u32				stat;
41562306a36Sopenharmony_ci#define DR_CPU_STAT_NOT_PRESENT		0x00
41662306a36Sopenharmony_ci#define DR_CPU_STAT_UNCONFIGURED	0x01
41762306a36Sopenharmony_ci#define DR_CPU_STAT_CONFIGURED		0x02
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	__u32				str_off;
42062306a36Sopenharmony_ci};
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic void __dr_cpu_send_error(struct ds_info *dp,
42362306a36Sopenharmony_ci				struct ds_cap_state *cp,
42462306a36Sopenharmony_ci				struct ds_data *data)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct dr_cpu_tag *tag = (struct dr_cpu_tag *) (data + 1);
42762306a36Sopenharmony_ci	struct {
42862306a36Sopenharmony_ci		struct ds_data		data;
42962306a36Sopenharmony_ci		struct dr_cpu_tag	tag;
43062306a36Sopenharmony_ci	} pkt;
43162306a36Sopenharmony_ci	int msg_len;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	memset(&pkt, 0, sizeof(pkt));
43462306a36Sopenharmony_ci	pkt.data.tag.type = DS_DATA;
43562306a36Sopenharmony_ci	pkt.data.handle = cp->handle;
43662306a36Sopenharmony_ci	pkt.tag.req_num = tag->req_num;
43762306a36Sopenharmony_ci	pkt.tag.type = DR_CPU_ERROR;
43862306a36Sopenharmony_ci	pkt.tag.num_records = 0;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	msg_len = (sizeof(struct ds_data) +
44162306a36Sopenharmony_ci		   sizeof(struct dr_cpu_tag));
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	pkt.data.tag.len = msg_len - sizeof(struct ds_msg_tag);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	__ds_send(dp->lp, &pkt, msg_len);
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic void dr_cpu_send_error(struct ds_info *dp,
44962306a36Sopenharmony_ci			      struct ds_cap_state *cp,
45062306a36Sopenharmony_ci			      struct ds_data *data)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	unsigned long flags;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	spin_lock_irqsave(&ds_lock, flags);
45562306a36Sopenharmony_ci	__dr_cpu_send_error(dp, cp, data);
45662306a36Sopenharmony_ci	spin_unlock_irqrestore(&ds_lock, flags);
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci#define CPU_SENTINEL	0xffffffff
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic void purge_dups(u32 *list, u32 num_ents)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	unsigned int i;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	for (i = 0; i < num_ents; i++) {
46662306a36Sopenharmony_ci		u32 cpu = list[i];
46762306a36Sopenharmony_ci		unsigned int j;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		if (cpu == CPU_SENTINEL)
47062306a36Sopenharmony_ci			continue;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		for (j = i + 1; j < num_ents; j++) {
47362306a36Sopenharmony_ci			if (list[j] == cpu)
47462306a36Sopenharmony_ci				list[j] = CPU_SENTINEL;
47562306a36Sopenharmony_ci		}
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic int dr_cpu_size_response(int ncpus)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	return (sizeof(struct ds_data) +
48262306a36Sopenharmony_ci		sizeof(struct dr_cpu_tag) +
48362306a36Sopenharmony_ci		(sizeof(struct dr_cpu_resp_entry) * ncpus));
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic void dr_cpu_init_response(struct ds_data *resp, u64 req_num,
48762306a36Sopenharmony_ci				 u64 handle, int resp_len, int ncpus,
48862306a36Sopenharmony_ci				 cpumask_t *mask, u32 default_stat)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct dr_cpu_resp_entry *ent;
49162306a36Sopenharmony_ci	struct dr_cpu_tag *tag;
49262306a36Sopenharmony_ci	int i, cpu;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	tag = (struct dr_cpu_tag *) (resp + 1);
49562306a36Sopenharmony_ci	ent = (struct dr_cpu_resp_entry *) (tag + 1);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	resp->tag.type = DS_DATA;
49862306a36Sopenharmony_ci	resp->tag.len = resp_len - sizeof(struct ds_msg_tag);
49962306a36Sopenharmony_ci	resp->handle = handle;
50062306a36Sopenharmony_ci	tag->req_num = req_num;
50162306a36Sopenharmony_ci	tag->type = DR_CPU_OK;
50262306a36Sopenharmony_ci	tag->num_records = ncpus;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	i = 0;
50562306a36Sopenharmony_ci	for_each_cpu(cpu, mask) {
50662306a36Sopenharmony_ci		ent[i].cpu = cpu;
50762306a36Sopenharmony_ci		ent[i].result = DR_CPU_RES_OK;
50862306a36Sopenharmony_ci		ent[i].stat = default_stat;
50962306a36Sopenharmony_ci		i++;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci	BUG_ON(i != ncpus);
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic void dr_cpu_mark(struct ds_data *resp, int cpu, int ncpus,
51562306a36Sopenharmony_ci			u32 res, u32 stat)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct dr_cpu_resp_entry *ent;
51862306a36Sopenharmony_ci	struct dr_cpu_tag *tag;
51962306a36Sopenharmony_ci	int i;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	tag = (struct dr_cpu_tag *) (resp + 1);
52262306a36Sopenharmony_ci	ent = (struct dr_cpu_resp_entry *) (tag + 1);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	for (i = 0; i < ncpus; i++) {
52562306a36Sopenharmony_ci		if (ent[i].cpu != cpu)
52662306a36Sopenharmony_ci			continue;
52762306a36Sopenharmony_ci		ent[i].result = res;
52862306a36Sopenharmony_ci		ent[i].stat = stat;
52962306a36Sopenharmony_ci		break;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic int dr_cpu_configure(struct ds_info *dp, struct ds_cap_state *cp,
53462306a36Sopenharmony_ci			    u64 req_num, cpumask_t *mask)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	struct ds_data *resp;
53762306a36Sopenharmony_ci	int resp_len, ncpus, cpu;
53862306a36Sopenharmony_ci	unsigned long flags;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	ncpus = cpumask_weight(mask);
54162306a36Sopenharmony_ci	resp_len = dr_cpu_size_response(ncpus);
54262306a36Sopenharmony_ci	resp = kzalloc(resp_len, GFP_KERNEL);
54362306a36Sopenharmony_ci	if (!resp)
54462306a36Sopenharmony_ci		return -ENOMEM;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	dr_cpu_init_response(resp, req_num, cp->handle,
54762306a36Sopenharmony_ci			     resp_len, ncpus, mask,
54862306a36Sopenharmony_ci			     DR_CPU_STAT_CONFIGURED);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	mdesc_populate_present_mask(mask);
55162306a36Sopenharmony_ci	mdesc_fill_in_cpu_data(mask);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	for_each_cpu(cpu, mask) {
55462306a36Sopenharmony_ci		int err;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		printk(KERN_INFO "ds-%llu: Starting cpu %d...\n",
55762306a36Sopenharmony_ci		       dp->id, cpu);
55862306a36Sopenharmony_ci		err = add_cpu(cpu);
55962306a36Sopenharmony_ci		if (err) {
56062306a36Sopenharmony_ci			__u32 res = DR_CPU_RES_FAILURE;
56162306a36Sopenharmony_ci			__u32 stat = DR_CPU_STAT_UNCONFIGURED;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci			if (!cpu_present(cpu)) {
56462306a36Sopenharmony_ci				/* CPU not present in MD */
56562306a36Sopenharmony_ci				res = DR_CPU_RES_NOT_IN_MD;
56662306a36Sopenharmony_ci				stat = DR_CPU_STAT_NOT_PRESENT;
56762306a36Sopenharmony_ci			} else if (err == -ENODEV) {
56862306a36Sopenharmony_ci				/* CPU did not call in successfully */
56962306a36Sopenharmony_ci				res = DR_CPU_RES_CPU_NOT_RESPONDING;
57062306a36Sopenharmony_ci			}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci			printk(KERN_INFO "ds-%llu: CPU startup failed err=%d\n",
57362306a36Sopenharmony_ci			       dp->id, err);
57462306a36Sopenharmony_ci			dr_cpu_mark(resp, cpu, ncpus, res, stat);
57562306a36Sopenharmony_ci		}
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	spin_lock_irqsave(&ds_lock, flags);
57962306a36Sopenharmony_ci	__ds_send(dp->lp, resp, resp_len);
58062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ds_lock, flags);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	kfree(resp);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/* Redistribute IRQs, taking into account the new cpus.  */
58562306a36Sopenharmony_ci	fixup_irqs();
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	return 0;
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic int dr_cpu_unconfigure(struct ds_info *dp,
59162306a36Sopenharmony_ci			      struct ds_cap_state *cp,
59262306a36Sopenharmony_ci			      u64 req_num,
59362306a36Sopenharmony_ci			      cpumask_t *mask)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	struct ds_data *resp;
59662306a36Sopenharmony_ci	int resp_len, ncpus, cpu;
59762306a36Sopenharmony_ci	unsigned long flags;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	ncpus = cpumask_weight(mask);
60062306a36Sopenharmony_ci	resp_len = dr_cpu_size_response(ncpus);
60162306a36Sopenharmony_ci	resp = kzalloc(resp_len, GFP_KERNEL);
60262306a36Sopenharmony_ci	if (!resp)
60362306a36Sopenharmony_ci		return -ENOMEM;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	dr_cpu_init_response(resp, req_num, cp->handle,
60662306a36Sopenharmony_ci			     resp_len, ncpus, mask,
60762306a36Sopenharmony_ci			     DR_CPU_STAT_UNCONFIGURED);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	for_each_cpu(cpu, mask) {
61062306a36Sopenharmony_ci		int err;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		printk(KERN_INFO "ds-%llu: Shutting down cpu %d...\n",
61362306a36Sopenharmony_ci		       dp->id, cpu);
61462306a36Sopenharmony_ci		err = remove_cpu(cpu);
61562306a36Sopenharmony_ci		if (err)
61662306a36Sopenharmony_ci			dr_cpu_mark(resp, cpu, ncpus,
61762306a36Sopenharmony_ci				    DR_CPU_RES_FAILURE,
61862306a36Sopenharmony_ci				    DR_CPU_STAT_CONFIGURED);
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	spin_lock_irqsave(&ds_lock, flags);
62262306a36Sopenharmony_ci	__ds_send(dp->lp, resp, resp_len);
62362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ds_lock, flags);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	kfree(resp);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	return 0;
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic void dr_cpu_data(struct ds_info *dp, struct ds_cap_state *cp, void *buf,
63162306a36Sopenharmony_ci			int len)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	struct ds_data *data = buf;
63462306a36Sopenharmony_ci	struct dr_cpu_tag *tag = (struct dr_cpu_tag *) (data + 1);
63562306a36Sopenharmony_ci	u32 *cpu_list = (u32 *) (tag + 1);
63662306a36Sopenharmony_ci	u64 req_num = tag->req_num;
63762306a36Sopenharmony_ci	cpumask_t mask;
63862306a36Sopenharmony_ci	unsigned int i;
63962306a36Sopenharmony_ci	int err;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	switch (tag->type) {
64262306a36Sopenharmony_ci	case DR_CPU_CONFIGURE:
64362306a36Sopenharmony_ci	case DR_CPU_UNCONFIGURE:
64462306a36Sopenharmony_ci	case DR_CPU_FORCE_UNCONFIGURE:
64562306a36Sopenharmony_ci		break;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	default:
64862306a36Sopenharmony_ci		dr_cpu_send_error(dp, cp, data);
64962306a36Sopenharmony_ci		return;
65062306a36Sopenharmony_ci	}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	purge_dups(cpu_list, tag->num_records);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	cpumask_clear(&mask);
65562306a36Sopenharmony_ci	for (i = 0; i < tag->num_records; i++) {
65662306a36Sopenharmony_ci		if (cpu_list[i] == CPU_SENTINEL)
65762306a36Sopenharmony_ci			continue;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci		if (cpu_list[i] < nr_cpu_ids)
66062306a36Sopenharmony_ci			cpumask_set_cpu(cpu_list[i], &mask);
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (tag->type == DR_CPU_CONFIGURE)
66462306a36Sopenharmony_ci		err = dr_cpu_configure(dp, cp, req_num, &mask);
66562306a36Sopenharmony_ci	else
66662306a36Sopenharmony_ci		err = dr_cpu_unconfigure(dp, cp, req_num, &mask);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (err)
66962306a36Sopenharmony_ci		dr_cpu_send_error(dp, cp, data);
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci#endif /* CONFIG_HOTPLUG_CPU */
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_cistruct ds_pri_msg {
67462306a36Sopenharmony_ci	__u64				req_num;
67562306a36Sopenharmony_ci	__u64				type;
67662306a36Sopenharmony_ci#define DS_PRI_REQUEST			0x00
67762306a36Sopenharmony_ci#define DS_PRI_DATA			0x01
67862306a36Sopenharmony_ci#define DS_PRI_UPDATE			0x02
67962306a36Sopenharmony_ci};
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_cistatic void ds_pri_data(struct ds_info *dp,
68262306a36Sopenharmony_ci			struct ds_cap_state *cp,
68362306a36Sopenharmony_ci			void *buf, int len)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct ds_data *dpkt = buf;
68662306a36Sopenharmony_ci	struct ds_pri_msg *rp;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	rp = (struct ds_pri_msg *) (dpkt + 1);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	printk(KERN_INFO "ds-%llu: PRI REQ [%llx:%llx], len=%d\n",
69162306a36Sopenharmony_ci	       dp->id, rp->req_num, rp->type, len);
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistruct ds_var_hdr {
69562306a36Sopenharmony_ci	__u32				type;
69662306a36Sopenharmony_ci#define DS_VAR_SET_REQ			0x00
69762306a36Sopenharmony_ci#define DS_VAR_DELETE_REQ		0x01
69862306a36Sopenharmony_ci#define DS_VAR_SET_RESP			0x02
69962306a36Sopenharmony_ci#define DS_VAR_DELETE_RESP		0x03
70062306a36Sopenharmony_ci};
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistruct ds_var_set_msg {
70362306a36Sopenharmony_ci	struct ds_var_hdr		hdr;
70462306a36Sopenharmony_ci	char				name_and_value[];
70562306a36Sopenharmony_ci};
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistruct ds_var_delete_msg {
70862306a36Sopenharmony_ci	struct ds_var_hdr		hdr;
70962306a36Sopenharmony_ci	char				name[];
71062306a36Sopenharmony_ci};
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistruct ds_var_resp {
71362306a36Sopenharmony_ci	struct ds_var_hdr		hdr;
71462306a36Sopenharmony_ci	__u32				result;
71562306a36Sopenharmony_ci#define DS_VAR_SUCCESS			0x00
71662306a36Sopenharmony_ci#define DS_VAR_NO_SPACE			0x01
71762306a36Sopenharmony_ci#define DS_VAR_INVALID_VAR		0x02
71862306a36Sopenharmony_ci#define DS_VAR_INVALID_VAL		0x03
71962306a36Sopenharmony_ci#define DS_VAR_NOT_PRESENT		0x04
72062306a36Sopenharmony_ci};
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cistatic DEFINE_MUTEX(ds_var_mutex);
72362306a36Sopenharmony_cistatic int ds_var_doorbell;
72462306a36Sopenharmony_cistatic int ds_var_response;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic void ds_var_data(struct ds_info *dp,
72762306a36Sopenharmony_ci			struct ds_cap_state *cp,
72862306a36Sopenharmony_ci			void *buf, int len)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	struct ds_data *dpkt = buf;
73162306a36Sopenharmony_ci	struct ds_var_resp *rp;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	rp = (struct ds_var_resp *) (dpkt + 1);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	if (rp->hdr.type != DS_VAR_SET_RESP &&
73662306a36Sopenharmony_ci	    rp->hdr.type != DS_VAR_DELETE_RESP)
73762306a36Sopenharmony_ci		return;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	ds_var_response = rp->result;
74062306a36Sopenharmony_ci	wmb();
74162306a36Sopenharmony_ci	ds_var_doorbell = 1;
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_civoid ldom_set_var(const char *var, const char *value)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	struct ds_cap_state *cp;
74762306a36Sopenharmony_ci	struct ds_info *dp;
74862306a36Sopenharmony_ci	unsigned long flags;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	spin_lock_irqsave(&ds_lock, flags);
75162306a36Sopenharmony_ci	cp = NULL;
75262306a36Sopenharmony_ci	for (dp = ds_info_list; dp; dp = dp->next) {
75362306a36Sopenharmony_ci		struct ds_cap_state *tmp;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci		tmp = find_cap_by_string(dp, "var-config");
75662306a36Sopenharmony_ci		if (tmp && tmp->state == CAP_STATE_REGISTERED) {
75762306a36Sopenharmony_ci			cp = tmp;
75862306a36Sopenharmony_ci			break;
75962306a36Sopenharmony_ci		}
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci	if (!cp) {
76262306a36Sopenharmony_ci		for (dp = ds_info_list; dp; dp = dp->next) {
76362306a36Sopenharmony_ci			struct ds_cap_state *tmp;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci			tmp = find_cap_by_string(dp, "var-config-backup");
76662306a36Sopenharmony_ci			if (tmp && tmp->state == CAP_STATE_REGISTERED) {
76762306a36Sopenharmony_ci				cp = tmp;
76862306a36Sopenharmony_ci				break;
76962306a36Sopenharmony_ci			}
77062306a36Sopenharmony_ci		}
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci	spin_unlock_irqrestore(&ds_lock, flags);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (cp) {
77562306a36Sopenharmony_ci		union {
77662306a36Sopenharmony_ci			struct {
77762306a36Sopenharmony_ci				struct ds_data		data;
77862306a36Sopenharmony_ci				struct ds_var_set_msg	msg;
77962306a36Sopenharmony_ci			} header;
78062306a36Sopenharmony_ci			char			all[512];
78162306a36Sopenharmony_ci		} pkt;
78262306a36Sopenharmony_ci		char  *base, *p;
78362306a36Sopenharmony_ci		int msg_len, loops;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		if (strlen(var) + strlen(value) + 2 >
78662306a36Sopenharmony_ci		    sizeof(pkt) - sizeof(pkt.header)) {
78762306a36Sopenharmony_ci			printk(KERN_ERR PFX
78862306a36Sopenharmony_ci				"contents length: %zu, which more than max: %lu,"
78962306a36Sopenharmony_ci				"so could not set (%s) variable to (%s).\n",
79062306a36Sopenharmony_ci				strlen(var) + strlen(value) + 2,
79162306a36Sopenharmony_ci				sizeof(pkt) - sizeof(pkt.header), var, value);
79262306a36Sopenharmony_ci			return;
79362306a36Sopenharmony_ci		}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		memset(&pkt, 0, sizeof(pkt));
79662306a36Sopenharmony_ci		pkt.header.data.tag.type = DS_DATA;
79762306a36Sopenharmony_ci		pkt.header.data.handle = cp->handle;
79862306a36Sopenharmony_ci		pkt.header.msg.hdr.type = DS_VAR_SET_REQ;
79962306a36Sopenharmony_ci		base = p = &pkt.header.msg.name_and_value[0];
80062306a36Sopenharmony_ci		strcpy(p, var);
80162306a36Sopenharmony_ci		p += strlen(var) + 1;
80262306a36Sopenharmony_ci		strcpy(p, value);
80362306a36Sopenharmony_ci		p += strlen(value) + 1;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci		msg_len = (sizeof(struct ds_data) +
80662306a36Sopenharmony_ci			   sizeof(struct ds_var_set_msg) +
80762306a36Sopenharmony_ci			   (p - base));
80862306a36Sopenharmony_ci		msg_len = (msg_len + 3) & ~3;
80962306a36Sopenharmony_ci		pkt.header.data.tag.len = msg_len - sizeof(struct ds_msg_tag);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci		mutex_lock(&ds_var_mutex);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci		spin_lock_irqsave(&ds_lock, flags);
81462306a36Sopenharmony_ci		ds_var_doorbell = 0;
81562306a36Sopenharmony_ci		ds_var_response = -1;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci		__ds_send(dp->lp, &pkt, msg_len);
81862306a36Sopenharmony_ci		spin_unlock_irqrestore(&ds_lock, flags);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci		loops = 1000;
82162306a36Sopenharmony_ci		while (ds_var_doorbell == 0) {
82262306a36Sopenharmony_ci			if (loops-- < 0)
82362306a36Sopenharmony_ci				break;
82462306a36Sopenharmony_ci			barrier();
82562306a36Sopenharmony_ci			udelay(100);
82662306a36Sopenharmony_ci		}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci		mutex_unlock(&ds_var_mutex);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci		if (ds_var_doorbell == 0 ||
83162306a36Sopenharmony_ci		    ds_var_response != DS_VAR_SUCCESS)
83262306a36Sopenharmony_ci			printk(KERN_ERR "ds-%llu: var-config [%s:%s] "
83362306a36Sopenharmony_ci			       "failed, response(%d).\n",
83462306a36Sopenharmony_ci			       dp->id, var, value,
83562306a36Sopenharmony_ci			       ds_var_response);
83662306a36Sopenharmony_ci	} else {
83762306a36Sopenharmony_ci		printk(KERN_ERR PFX "var-config not registered so "
83862306a36Sopenharmony_ci		       "could not set (%s) variable to (%s).\n",
83962306a36Sopenharmony_ci		       var, value);
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cistatic char full_boot_str[256] __attribute__((aligned(32)));
84462306a36Sopenharmony_cistatic int reboot_data_supported;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_civoid ldom_reboot(const char *boot_command)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	/* Don't bother with any of this if the boot_command
84962306a36Sopenharmony_ci	 * is empty.
85062306a36Sopenharmony_ci	 */
85162306a36Sopenharmony_ci	if (boot_command && strlen(boot_command)) {
85262306a36Sopenharmony_ci		unsigned long len;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci		snprintf(full_boot_str, sizeof(full_boot_str), "boot %s",
85562306a36Sopenharmony_ci			 boot_command);
85662306a36Sopenharmony_ci		len = strlen(full_boot_str);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci		if (reboot_data_supported) {
85962306a36Sopenharmony_ci			unsigned long ra = kimage_addr_to_ra(full_boot_str);
86062306a36Sopenharmony_ci			unsigned long hv_ret;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci			hv_ret = sun4v_reboot_data_set(ra, len);
86362306a36Sopenharmony_ci			if (hv_ret != HV_EOK)
86462306a36Sopenharmony_ci				pr_err("SUN4V: Unable to set reboot data "
86562306a36Sopenharmony_ci				       "hv_ret=%lu\n", hv_ret);
86662306a36Sopenharmony_ci		} else {
86762306a36Sopenharmony_ci			ldom_set_var("reboot-command", full_boot_str);
86862306a36Sopenharmony_ci		}
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci	sun4v_mach_sir();
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_civoid ldom_power_off(void)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	sun4v_mach_exit(0);
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic void ds_conn_reset(struct ds_info *dp)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci	printk(KERN_ERR "ds-%llu: ds_conn_reset() from %ps\n",
88162306a36Sopenharmony_ci	       dp->id, __builtin_return_address(0));
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_cistatic int register_services(struct ds_info *dp)
88562306a36Sopenharmony_ci{
88662306a36Sopenharmony_ci	struct ldc_channel *lp = dp->lp;
88762306a36Sopenharmony_ci	int i;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	for (i = 0; i < dp->num_ds_states; i++) {
89062306a36Sopenharmony_ci		struct {
89162306a36Sopenharmony_ci			struct ds_reg_req req;
89262306a36Sopenharmony_ci			u8 id_buf[256];
89362306a36Sopenharmony_ci		} pbuf;
89462306a36Sopenharmony_ci		struct ds_cap_state *cp = &dp->ds_states[i];
89562306a36Sopenharmony_ci		int err, msg_len;
89662306a36Sopenharmony_ci		u64 new_count;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci		if (cp->state == CAP_STATE_REGISTERED)
89962306a36Sopenharmony_ci			continue;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci		new_count = sched_clock() & 0xffffffff;
90262306a36Sopenharmony_ci		cp->handle = ((u64) i << 32) | new_count;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci		msg_len = (sizeof(struct ds_reg_req) +
90562306a36Sopenharmony_ci			   strlen(cp->service_id));
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		memset(&pbuf, 0, sizeof(pbuf));
90862306a36Sopenharmony_ci		pbuf.req.tag.type = DS_REG_REQ;
90962306a36Sopenharmony_ci		pbuf.req.tag.len = (msg_len - sizeof(struct ds_msg_tag));
91062306a36Sopenharmony_ci		pbuf.req.handle = cp->handle;
91162306a36Sopenharmony_ci		pbuf.req.major = 1;
91262306a36Sopenharmony_ci		pbuf.req.minor = 0;
91362306a36Sopenharmony_ci		strcpy(pbuf.id_buf, cp->service_id);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci		err = __ds_send(lp, &pbuf, msg_len);
91662306a36Sopenharmony_ci		if (err > 0)
91762306a36Sopenharmony_ci			cp->state = CAP_STATE_REG_SENT;
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci	return 0;
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_cistatic int ds_handshake(struct ds_info *dp, struct ds_msg_tag *pkt)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	if (dp->hs_state == DS_HS_START) {
92662306a36Sopenharmony_ci		if (pkt->type != DS_INIT_ACK)
92762306a36Sopenharmony_ci			goto conn_reset;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci		dp->hs_state = DS_HS_DONE;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci		return register_services(dp);
93262306a36Sopenharmony_ci	}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	if (dp->hs_state != DS_HS_DONE)
93562306a36Sopenharmony_ci		goto conn_reset;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	if (pkt->type == DS_REG_ACK) {
93862306a36Sopenharmony_ci		struct ds_reg_ack *ap = (struct ds_reg_ack *) pkt;
93962306a36Sopenharmony_ci		struct ds_cap_state *cp = find_cap(dp, ap->handle);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci		if (!cp) {
94262306a36Sopenharmony_ci			printk(KERN_ERR "ds-%llu: REG ACK for unknown "
94362306a36Sopenharmony_ci			       "handle %llx\n", dp->id, ap->handle);
94462306a36Sopenharmony_ci			return 0;
94562306a36Sopenharmony_ci		}
94662306a36Sopenharmony_ci		printk(KERN_INFO "ds-%llu: Registered %s service.\n",
94762306a36Sopenharmony_ci		       dp->id, cp->service_id);
94862306a36Sopenharmony_ci		cp->state = CAP_STATE_REGISTERED;
94962306a36Sopenharmony_ci	} else if (pkt->type == DS_REG_NACK) {
95062306a36Sopenharmony_ci		struct ds_reg_nack *np = (struct ds_reg_nack *) pkt;
95162306a36Sopenharmony_ci		struct ds_cap_state *cp = find_cap(dp, np->handle);
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci		if (!cp) {
95462306a36Sopenharmony_ci			printk(KERN_ERR "ds-%llu: REG NACK for "
95562306a36Sopenharmony_ci			       "unknown handle %llx\n",
95662306a36Sopenharmony_ci			       dp->id, np->handle);
95762306a36Sopenharmony_ci			return 0;
95862306a36Sopenharmony_ci		}
95962306a36Sopenharmony_ci		cp->state = CAP_STATE_UNKNOWN;
96062306a36Sopenharmony_ci	}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	return 0;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ciconn_reset:
96562306a36Sopenharmony_ci	ds_conn_reset(dp);
96662306a36Sopenharmony_ci	return -ECONNRESET;
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic void __send_ds_nack(struct ds_info *dp, u64 handle)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	struct ds_data_nack nack = {
97262306a36Sopenharmony_ci		.tag = {
97362306a36Sopenharmony_ci			.type = DS_NACK,
97462306a36Sopenharmony_ci			.len = (sizeof(struct ds_data_nack) -
97562306a36Sopenharmony_ci				sizeof(struct ds_msg_tag)),
97662306a36Sopenharmony_ci		},
97762306a36Sopenharmony_ci		.handle = handle,
97862306a36Sopenharmony_ci		.result = DS_INV_HDL,
97962306a36Sopenharmony_ci	};
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	__ds_send(dp->lp, &nack, sizeof(nack));
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic LIST_HEAD(ds_work_list);
98562306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(ds_wait);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_cistruct ds_queue_entry {
98862306a36Sopenharmony_ci	struct list_head		list;
98962306a36Sopenharmony_ci	struct ds_info			*dp;
99062306a36Sopenharmony_ci	int				req_len;
99162306a36Sopenharmony_ci	int				__pad;
99262306a36Sopenharmony_ci	u64				req[];
99362306a36Sopenharmony_ci};
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_cistatic void process_ds_work(void)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	struct ds_queue_entry *qp, *tmp;
99862306a36Sopenharmony_ci	unsigned long flags;
99962306a36Sopenharmony_ci	LIST_HEAD(todo);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	spin_lock_irqsave(&ds_lock, flags);
100262306a36Sopenharmony_ci	list_splice_init(&ds_work_list, &todo);
100362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ds_lock, flags);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	list_for_each_entry_safe(qp, tmp, &todo, list) {
100662306a36Sopenharmony_ci		struct ds_data *dpkt = (struct ds_data *) qp->req;
100762306a36Sopenharmony_ci		struct ds_info *dp = qp->dp;
100862306a36Sopenharmony_ci		struct ds_cap_state *cp = find_cap(dp, dpkt->handle);
100962306a36Sopenharmony_ci		int req_len = qp->req_len;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci		if (!cp) {
101262306a36Sopenharmony_ci			printk(KERN_ERR "ds-%llu: Data for unknown "
101362306a36Sopenharmony_ci			       "handle %llu\n",
101462306a36Sopenharmony_ci			       dp->id, dpkt->handle);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci			spin_lock_irqsave(&ds_lock, flags);
101762306a36Sopenharmony_ci			__send_ds_nack(dp, dpkt->handle);
101862306a36Sopenharmony_ci			spin_unlock_irqrestore(&ds_lock, flags);
101962306a36Sopenharmony_ci		} else {
102062306a36Sopenharmony_ci			cp->data(dp, cp, dpkt, req_len);
102162306a36Sopenharmony_ci		}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci		list_del(&qp->list);
102462306a36Sopenharmony_ci		kfree(qp);
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic int ds_thread(void *__unused)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	DEFINE_WAIT(wait);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	while (1) {
103362306a36Sopenharmony_ci		prepare_to_wait(&ds_wait, &wait, TASK_INTERRUPTIBLE);
103462306a36Sopenharmony_ci		if (list_empty(&ds_work_list))
103562306a36Sopenharmony_ci			schedule();
103662306a36Sopenharmony_ci		finish_wait(&ds_wait, &wait);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci		if (kthread_should_stop())
103962306a36Sopenharmony_ci			break;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci		process_ds_work();
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	return 0;
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_cistatic int ds_data(struct ds_info *dp, struct ds_msg_tag *pkt, int len)
104862306a36Sopenharmony_ci{
104962306a36Sopenharmony_ci	struct ds_data *dpkt = (struct ds_data *) pkt;
105062306a36Sopenharmony_ci	struct ds_queue_entry *qp;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	qp = kmalloc(sizeof(struct ds_queue_entry) + len, GFP_ATOMIC);
105362306a36Sopenharmony_ci	if (!qp) {
105462306a36Sopenharmony_ci		__send_ds_nack(dp, dpkt->handle);
105562306a36Sopenharmony_ci	} else {
105662306a36Sopenharmony_ci		qp->dp = dp;
105762306a36Sopenharmony_ci		memcpy(&qp->req, pkt, len);
105862306a36Sopenharmony_ci		list_add_tail(&qp->list, &ds_work_list);
105962306a36Sopenharmony_ci		wake_up(&ds_wait);
106062306a36Sopenharmony_ci	}
106162306a36Sopenharmony_ci	return 0;
106262306a36Sopenharmony_ci}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_cistatic void ds_up(struct ds_info *dp)
106562306a36Sopenharmony_ci{
106662306a36Sopenharmony_ci	struct ldc_channel *lp = dp->lp;
106762306a36Sopenharmony_ci	struct ds_ver_req req;
106862306a36Sopenharmony_ci	int err;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	req.tag.type = DS_INIT_REQ;
107162306a36Sopenharmony_ci	req.tag.len = sizeof(req) - sizeof(struct ds_msg_tag);
107262306a36Sopenharmony_ci	req.ver.major = 1;
107362306a36Sopenharmony_ci	req.ver.minor = 0;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	err = __ds_send(lp, &req, sizeof(req));
107662306a36Sopenharmony_ci	if (err > 0)
107762306a36Sopenharmony_ci		dp->hs_state = DS_HS_START;
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_cistatic void ds_reset(struct ds_info *dp)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	int i;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	dp->hs_state = 0;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	for (i = 0; i < dp->num_ds_states; i++) {
108762306a36Sopenharmony_ci		struct ds_cap_state *cp = &dp->ds_states[i];
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci		cp->state = CAP_STATE_UNKNOWN;
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_cistatic void ds_event(void *arg, int event)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	struct ds_info *dp = arg;
109662306a36Sopenharmony_ci	struct ldc_channel *lp = dp->lp;
109762306a36Sopenharmony_ci	unsigned long flags;
109862306a36Sopenharmony_ci	int err;
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	spin_lock_irqsave(&ds_lock, flags);
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	if (event == LDC_EVENT_UP) {
110362306a36Sopenharmony_ci		ds_up(dp);
110462306a36Sopenharmony_ci		spin_unlock_irqrestore(&ds_lock, flags);
110562306a36Sopenharmony_ci		return;
110662306a36Sopenharmony_ci	}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	if (event == LDC_EVENT_RESET) {
110962306a36Sopenharmony_ci		ds_reset(dp);
111062306a36Sopenharmony_ci		spin_unlock_irqrestore(&ds_lock, flags);
111162306a36Sopenharmony_ci		return;
111262306a36Sopenharmony_ci	}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	if (event != LDC_EVENT_DATA_READY) {
111562306a36Sopenharmony_ci		printk(KERN_WARNING "ds-%llu: Unexpected LDC event %d\n",
111662306a36Sopenharmony_ci		       dp->id, event);
111762306a36Sopenharmony_ci		spin_unlock_irqrestore(&ds_lock, flags);
111862306a36Sopenharmony_ci		return;
111962306a36Sopenharmony_ci	}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	err = 0;
112262306a36Sopenharmony_ci	while (1) {
112362306a36Sopenharmony_ci		struct ds_msg_tag *tag;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci		err = ldc_read(lp, dp->rcv_buf, sizeof(*tag));
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci		if (unlikely(err < 0)) {
112862306a36Sopenharmony_ci			if (err == -ECONNRESET)
112962306a36Sopenharmony_ci				ds_conn_reset(dp);
113062306a36Sopenharmony_ci			break;
113162306a36Sopenharmony_ci		}
113262306a36Sopenharmony_ci		if (err == 0)
113362306a36Sopenharmony_ci			break;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci		tag = dp->rcv_buf;
113662306a36Sopenharmony_ci		err = ldc_read(lp, tag + 1, tag->len);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci		if (unlikely(err < 0)) {
113962306a36Sopenharmony_ci			if (err == -ECONNRESET)
114062306a36Sopenharmony_ci				ds_conn_reset(dp);
114162306a36Sopenharmony_ci			break;
114262306a36Sopenharmony_ci		}
114362306a36Sopenharmony_ci		if (err < tag->len)
114462306a36Sopenharmony_ci			break;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci		if (tag->type < DS_DATA)
114762306a36Sopenharmony_ci			err = ds_handshake(dp, dp->rcv_buf);
114862306a36Sopenharmony_ci		else
114962306a36Sopenharmony_ci			err = ds_data(dp, dp->rcv_buf,
115062306a36Sopenharmony_ci				      sizeof(*tag) + err);
115162306a36Sopenharmony_ci		if (err == -ECONNRESET)
115262306a36Sopenharmony_ci			break;
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	spin_unlock_irqrestore(&ds_lock, flags);
115662306a36Sopenharmony_ci}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_cistatic int ds_probe(struct vio_dev *vdev, const struct vio_device_id *id)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci	static int ds_version_printed;
116162306a36Sopenharmony_ci	struct ldc_channel_config ds_cfg = {
116262306a36Sopenharmony_ci		.event		= ds_event,
116362306a36Sopenharmony_ci		.mtu		= 4096,
116462306a36Sopenharmony_ci		.mode		= LDC_MODE_STREAM,
116562306a36Sopenharmony_ci	};
116662306a36Sopenharmony_ci	struct mdesc_handle *hp;
116762306a36Sopenharmony_ci	struct ldc_channel *lp;
116862306a36Sopenharmony_ci	struct ds_info *dp;
116962306a36Sopenharmony_ci	const u64 *val;
117062306a36Sopenharmony_ci	int err, i;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	if (ds_version_printed++ == 0)
117362306a36Sopenharmony_ci		printk(KERN_INFO "%s", version);
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	dp = kzalloc(sizeof(*dp), GFP_KERNEL);
117662306a36Sopenharmony_ci	err = -ENOMEM;
117762306a36Sopenharmony_ci	if (!dp)
117862306a36Sopenharmony_ci		goto out_err;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	hp = mdesc_grab();
118162306a36Sopenharmony_ci	val = mdesc_get_property(hp, vdev->mp, "id", NULL);
118262306a36Sopenharmony_ci	if (val)
118362306a36Sopenharmony_ci		dp->id = *val;
118462306a36Sopenharmony_ci	mdesc_release(hp);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	dp->rcv_buf = kzalloc(4096, GFP_KERNEL);
118762306a36Sopenharmony_ci	if (!dp->rcv_buf)
118862306a36Sopenharmony_ci		goto out_free_dp;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	dp->rcv_buf_len = 4096;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	dp->ds_states = kmemdup(ds_states_template,
119362306a36Sopenharmony_ci				sizeof(ds_states_template), GFP_KERNEL);
119462306a36Sopenharmony_ci	if (!dp->ds_states)
119562306a36Sopenharmony_ci		goto out_free_rcv_buf;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	dp->num_ds_states = ARRAY_SIZE(ds_states_template);
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	for (i = 0; i < dp->num_ds_states; i++)
120062306a36Sopenharmony_ci		dp->ds_states[i].handle = ((u64)i << 32);
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	ds_cfg.tx_irq = vdev->tx_irq;
120362306a36Sopenharmony_ci	ds_cfg.rx_irq = vdev->rx_irq;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	lp = ldc_alloc(vdev->channel_id, &ds_cfg, dp, "DS");
120662306a36Sopenharmony_ci	if (IS_ERR(lp)) {
120762306a36Sopenharmony_ci		err = PTR_ERR(lp);
120862306a36Sopenharmony_ci		goto out_free_ds_states;
120962306a36Sopenharmony_ci	}
121062306a36Sopenharmony_ci	dp->lp = lp;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	err = ldc_bind(lp);
121362306a36Sopenharmony_ci	if (err)
121462306a36Sopenharmony_ci		goto out_free_ldc;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	spin_lock_irq(&ds_lock);
121762306a36Sopenharmony_ci	dp->next = ds_info_list;
121862306a36Sopenharmony_ci	ds_info_list = dp;
121962306a36Sopenharmony_ci	spin_unlock_irq(&ds_lock);
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	return err;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ciout_free_ldc:
122462306a36Sopenharmony_ci	ldc_free(dp->lp);
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ciout_free_ds_states:
122762306a36Sopenharmony_ci	kfree(dp->ds_states);
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ciout_free_rcv_buf:
123062306a36Sopenharmony_ci	kfree(dp->rcv_buf);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ciout_free_dp:
123362306a36Sopenharmony_ci	kfree(dp);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ciout_err:
123662306a36Sopenharmony_ci	return err;
123762306a36Sopenharmony_ci}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_cistatic const struct vio_device_id ds_match[] = {
124062306a36Sopenharmony_ci	{
124162306a36Sopenharmony_ci		.type = "domain-services-port",
124262306a36Sopenharmony_ci	},
124362306a36Sopenharmony_ci	{},
124462306a36Sopenharmony_ci};
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_cistatic struct vio_driver ds_driver = {
124762306a36Sopenharmony_ci	.id_table	= ds_match,
124862306a36Sopenharmony_ci	.probe		= ds_probe,
124962306a36Sopenharmony_ci	.name		= "ds",
125062306a36Sopenharmony_ci};
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_cistatic int __init ds_init(void)
125362306a36Sopenharmony_ci{
125462306a36Sopenharmony_ci	unsigned long hv_ret, major, minor;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	if (tlb_type == hypervisor) {
125762306a36Sopenharmony_ci		hv_ret = sun4v_get_version(HV_GRP_REBOOT_DATA, &major, &minor);
125862306a36Sopenharmony_ci		if (hv_ret == HV_EOK) {
125962306a36Sopenharmony_ci			pr_info("SUN4V: Reboot data supported (maj=%lu,min=%lu).\n",
126062306a36Sopenharmony_ci				major, minor);
126162306a36Sopenharmony_ci			reboot_data_supported = 1;
126262306a36Sopenharmony_ci		}
126362306a36Sopenharmony_ci	}
126462306a36Sopenharmony_ci	kthread_run(ds_thread, NULL, "kldomd");
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	return vio_register_driver(&ds_driver);
126762306a36Sopenharmony_ci}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_cifs_initcall(ds_init);
1270