xref: /openbmc/linux/drivers/clk/hisilicon/clk.c (revision a96cbb14)
116216333SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20aa0c95fSHaojian Zhuang /*
30aa0c95fSHaojian Zhuang  * Hisilicon clock driver
40aa0c95fSHaojian Zhuang  *
50aa0c95fSHaojian Zhuang  * Copyright (c) 2012-2013 Hisilicon Limited.
60aa0c95fSHaojian Zhuang  * Copyright (c) 2012-2013 Linaro Limited.
70aa0c95fSHaojian Zhuang  *
80aa0c95fSHaojian Zhuang  * Author: Haojian Zhuang <haojian.zhuang@linaro.org>
90aa0c95fSHaojian Zhuang  *	   Xin Li <li.xin@linaro.org>
100aa0c95fSHaojian Zhuang  */
110aa0c95fSHaojian Zhuang 
120aa0c95fSHaojian Zhuang #include <linux/kernel.h>
130aa0c95fSHaojian Zhuang #include <linux/clkdev.h>
14593438e4SStephen Boyd #include <linux/clk-provider.h>
150aa0c95fSHaojian Zhuang #include <linux/delay.h>
160aa0c95fSHaojian Zhuang #include <linux/io.h>
170aa0c95fSHaojian Zhuang #include <linux/of.h>
180aa0c95fSHaojian Zhuang #include <linux/of_address.h>
19*a96cbb14SRob Herring #include <linux/platform_device.h>
200aa0c95fSHaojian Zhuang #include <linux/slab.h>
210aa0c95fSHaojian Zhuang 
220aa0c95fSHaojian Zhuang #include "clk.h"
230aa0c95fSHaojian Zhuang 
240aa0c95fSHaojian Zhuang static DEFINE_SPINLOCK(hisi_clk_lock);
250aa0c95fSHaojian Zhuang 
hisi_clk_alloc(struct platform_device * pdev,int nr_clks)2632226916SJiancheng Xue struct hisi_clock_data *hisi_clk_alloc(struct platform_device *pdev,
2732226916SJiancheng Xue 						int nr_clks)
2832226916SJiancheng Xue {
2932226916SJiancheng Xue 	struct hisi_clock_data *clk_data;
3032226916SJiancheng Xue 	struct resource *res;
3132226916SJiancheng Xue 	struct clk **clk_table;
3232226916SJiancheng Xue 
3332226916SJiancheng Xue 	clk_data = devm_kmalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL);
3432226916SJiancheng Xue 	if (!clk_data)
3532226916SJiancheng Xue 		return NULL;
3632226916SJiancheng Xue 
3732226916SJiancheng Xue 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
38c744b63bSWei Yongjun 	if (!res)
39c744b63bSWei Yongjun 		return NULL;
4032226916SJiancheng Xue 	clk_data->base = devm_ioremap(&pdev->dev,
4132226916SJiancheng Xue 				res->start, resource_size(res));
4232226916SJiancheng Xue 	if (!clk_data->base)
4332226916SJiancheng Xue 		return NULL;
4432226916SJiancheng Xue 
458d9bdc46SMarkus Elfring 	clk_table = devm_kmalloc_array(&pdev->dev, nr_clks,
468d9bdc46SMarkus Elfring 				       sizeof(*clk_table),
4732226916SJiancheng Xue 				       GFP_KERNEL);
4832226916SJiancheng Xue 	if (!clk_table)
4932226916SJiancheng Xue 		return NULL;
5032226916SJiancheng Xue 
5132226916SJiancheng Xue 	clk_data->clk_data.clks = clk_table;
5232226916SJiancheng Xue 	clk_data->clk_data.clk_num = nr_clks;
5332226916SJiancheng Xue 
5432226916SJiancheng Xue 	return clk_data;
5532226916SJiancheng Xue }
5632226916SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_alloc);
5732226916SJiancheng Xue 
hisi_clk_init(struct device_node * np,int nr_clks)58f6ff57c8SJiancheng Xue struct hisi_clock_data *hisi_clk_init(struct device_node *np,
5975af25f5SHaojian Zhuang 					     int nr_clks)
600aa0c95fSHaojian Zhuang {
6175af25f5SHaojian Zhuang 	struct hisi_clock_data *clk_data;
6275af25f5SHaojian Zhuang 	struct clk **clk_table;
6375af25f5SHaojian Zhuang 	void __iomem *base;
6475af25f5SHaojian Zhuang 
6575af25f5SHaojian Zhuang 	base = of_iomap(np, 0);
6675af25f5SHaojian Zhuang 	if (!base) {
671fb6dd9dSLeo Yan 		pr_err("%s: failed to map clock registers\n", __func__);
6875af25f5SHaojian Zhuang 		goto err;
6975af25f5SHaojian Zhuang 	}
7075af25f5SHaojian Zhuang 
7175af25f5SHaojian Zhuang 	clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
72840e5632SMarkus Elfring 	if (!clk_data)
7375af25f5SHaojian Zhuang 		goto err;
74840e5632SMarkus Elfring 
7575af25f5SHaojian Zhuang 	clk_data->base = base;
767b9bae17SMarkus Elfring 	clk_table = kcalloc(nr_clks, sizeof(*clk_table), GFP_KERNEL);
77840e5632SMarkus Elfring 	if (!clk_table)
7875af25f5SHaojian Zhuang 		goto err_data;
79840e5632SMarkus Elfring 
8075af25f5SHaojian Zhuang 	clk_data->clk_data.clks = clk_table;
8175af25f5SHaojian Zhuang 	clk_data->clk_data.clk_num = nr_clks;
8275af25f5SHaojian Zhuang 	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data->clk_data);
8375af25f5SHaojian Zhuang 	return clk_data;
8475af25f5SHaojian Zhuang err_data:
8575af25f5SHaojian Zhuang 	kfree(clk_data);
8675af25f5SHaojian Zhuang err:
8775af25f5SHaojian Zhuang 	return NULL;
880aa0c95fSHaojian Zhuang }
89f6ff57c8SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_init);
900aa0c95fSHaojian Zhuang 
hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock * clks,int nums,struct hisi_clock_data * data)915497f668SJiancheng Xue int hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock *clks,
9275af25f5SHaojian Zhuang 					 int nums, struct hisi_clock_data *data)
930aa0c95fSHaojian Zhuang {
940aa0c95fSHaojian Zhuang 	struct clk *clk;
950aa0c95fSHaojian Zhuang 	int i;
960aa0c95fSHaojian Zhuang 
970aa0c95fSHaojian Zhuang 	for (i = 0; i < nums; i++) {
980aa0c95fSHaojian Zhuang 		clk = clk_register_fixed_rate(NULL, clks[i].name,
990aa0c95fSHaojian Zhuang 					      clks[i].parent_name,
1000aa0c95fSHaojian Zhuang 					      clks[i].flags,
1010aa0c95fSHaojian Zhuang 					      clks[i].fixed_rate);
1020aa0c95fSHaojian Zhuang 		if (IS_ERR(clk)) {
1030aa0c95fSHaojian Zhuang 			pr_err("%s: failed to register clock %s\n",
1040aa0c95fSHaojian Zhuang 			       __func__, clks[i].name);
1055497f668SJiancheng Xue 			goto err;
1060aa0c95fSHaojian Zhuang 		}
10775af25f5SHaojian Zhuang 		data->clk_data.clks[clks[i].id] = clk;
1080aa0c95fSHaojian Zhuang 	}
1095497f668SJiancheng Xue 
1105497f668SJiancheng Xue 	return 0;
1115497f668SJiancheng Xue 
1125497f668SJiancheng Xue err:
1135497f668SJiancheng Xue 	while (i--)
1145497f668SJiancheng Xue 		clk_unregister_fixed_rate(data->clk_data.clks[clks[i].id]);
1155497f668SJiancheng Xue 
1165497f668SJiancheng Xue 	return PTR_ERR(clk);
1170aa0c95fSHaojian Zhuang }
118f6ff57c8SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_rate);
1190aa0c95fSHaojian Zhuang 
hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock * clks,int nums,struct hisi_clock_data * data)1205497f668SJiancheng Xue int hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock *clks,
12175af25f5SHaojian Zhuang 					   int nums,
12275af25f5SHaojian Zhuang 					   struct hisi_clock_data *data)
1230aa0c95fSHaojian Zhuang {
1240aa0c95fSHaojian Zhuang 	struct clk *clk;
1250aa0c95fSHaojian Zhuang 	int i;
1260aa0c95fSHaojian Zhuang 
1270aa0c95fSHaojian Zhuang 	for (i = 0; i < nums; i++) {
1280aa0c95fSHaojian Zhuang 		clk = clk_register_fixed_factor(NULL, clks[i].name,
1290aa0c95fSHaojian Zhuang 						clks[i].parent_name,
1300aa0c95fSHaojian Zhuang 						clks[i].flags, clks[i].mult,
1310aa0c95fSHaojian Zhuang 						clks[i].div);
1320aa0c95fSHaojian Zhuang 		if (IS_ERR(clk)) {
1330aa0c95fSHaojian Zhuang 			pr_err("%s: failed to register clock %s\n",
1340aa0c95fSHaojian Zhuang 			       __func__, clks[i].name);
1355497f668SJiancheng Xue 			goto err;
1360aa0c95fSHaojian Zhuang 		}
13775af25f5SHaojian Zhuang 		data->clk_data.clks[clks[i].id] = clk;
1380aa0c95fSHaojian Zhuang 	}
1395497f668SJiancheng Xue 
1405497f668SJiancheng Xue 	return 0;
1415497f668SJiancheng Xue 
1425497f668SJiancheng Xue err:
1435497f668SJiancheng Xue 	while (i--)
1445497f668SJiancheng Xue 		clk_unregister_fixed_factor(data->clk_data.clks[clks[i].id]);
1455497f668SJiancheng Xue 
1465497f668SJiancheng Xue 	return PTR_ERR(clk);
1470aa0c95fSHaojian Zhuang }
148f6ff57c8SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_factor);
1490aa0c95fSHaojian Zhuang 
hisi_clk_register_mux(const struct hisi_mux_clock * clks,int nums,struct hisi_clock_data * data)1505497f668SJiancheng Xue int hisi_clk_register_mux(const struct hisi_mux_clock *clks,
15175af25f5SHaojian Zhuang 				  int nums, struct hisi_clock_data *data)
1520aa0c95fSHaojian Zhuang {
1530aa0c95fSHaojian Zhuang 	struct clk *clk;
15475af25f5SHaojian Zhuang 	void __iomem *base = data->base;
1550aa0c95fSHaojian Zhuang 	int i;
1560aa0c95fSHaojian Zhuang 
1570aa0c95fSHaojian Zhuang 	for (i = 0; i < nums; i++) {
158156342a1SZhangfei Gao 		u32 mask = BIT(clks[i].width) - 1;
159156342a1SZhangfei Gao 
160156342a1SZhangfei Gao 		clk = clk_register_mux_table(NULL, clks[i].name,
161156342a1SZhangfei Gao 					clks[i].parent_names,
1620aa0c95fSHaojian Zhuang 					clks[i].num_parents, clks[i].flags,
1630aa0c95fSHaojian Zhuang 					base + clks[i].offset, clks[i].shift,
164156342a1SZhangfei Gao 					mask, clks[i].mux_flags,
16508edf704SJonathan Neuschäfer 					clks[i].table, &hisi_clk_lock);
1660aa0c95fSHaojian Zhuang 		if (IS_ERR(clk)) {
1670aa0c95fSHaojian Zhuang 			pr_err("%s: failed to register clock %s\n",
1680aa0c95fSHaojian Zhuang 			       __func__, clks[i].name);
1695497f668SJiancheng Xue 			goto err;
1700aa0c95fSHaojian Zhuang 		}
1710aa0c95fSHaojian Zhuang 
1720aa0c95fSHaojian Zhuang 		if (clks[i].alias)
1730aa0c95fSHaojian Zhuang 			clk_register_clkdev(clk, clks[i].alias, NULL);
1740aa0c95fSHaojian Zhuang 
17575af25f5SHaojian Zhuang 		data->clk_data.clks[clks[i].id] = clk;
1760aa0c95fSHaojian Zhuang 	}
1775497f668SJiancheng Xue 
1785497f668SJiancheng Xue 	return 0;
1795497f668SJiancheng Xue 
1805497f668SJiancheng Xue err:
1815497f668SJiancheng Xue 	while (i--)
1825497f668SJiancheng Xue 		clk_unregister_mux(data->clk_data.clks[clks[i].id]);
1835497f668SJiancheng Xue 
1845497f668SJiancheng Xue 	return PTR_ERR(clk);
1850aa0c95fSHaojian Zhuang }
186f6ff57c8SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_register_mux);
1870aa0c95fSHaojian Zhuang 
hisi_clk_register_phase(struct device * dev,const struct hisi_phase_clock * clks,int nums,struct hisi_clock_data * data)188811f67ccStianshuliang int hisi_clk_register_phase(struct device *dev,
189811f67ccStianshuliang 			    const struct hisi_phase_clock *clks,
190811f67ccStianshuliang 			    int nums, struct hisi_clock_data *data)
191811f67ccStianshuliang {
192811f67ccStianshuliang 	void __iomem *base = data->base;
193811f67ccStianshuliang 	struct clk *clk;
194811f67ccStianshuliang 	int i;
195811f67ccStianshuliang 
196811f67ccStianshuliang 	for (i = 0; i < nums; i++) {
197811f67ccStianshuliang 		clk = clk_register_hisi_phase(dev, &clks[i], base,
198811f67ccStianshuliang 					      &hisi_clk_lock);
199811f67ccStianshuliang 		if (IS_ERR(clk)) {
200811f67ccStianshuliang 			pr_err("%s: failed to register clock %s\n", __func__,
201811f67ccStianshuliang 			       clks[i].name);
202811f67ccStianshuliang 			return PTR_ERR(clk);
203811f67ccStianshuliang 		}
204811f67ccStianshuliang 
205811f67ccStianshuliang 		data->clk_data.clks[clks[i].id] = clk;
206811f67ccStianshuliang 	}
207811f67ccStianshuliang 
208811f67ccStianshuliang 	return 0;
209811f67ccStianshuliang }
210811f67ccStianshuliang EXPORT_SYMBOL_GPL(hisi_clk_register_phase);
211811f67ccStianshuliang 
hisi_clk_register_divider(const struct hisi_divider_clock * clks,int nums,struct hisi_clock_data * data)2125497f668SJiancheng Xue int hisi_clk_register_divider(const struct hisi_divider_clock *clks,
21375af25f5SHaojian Zhuang 				      int nums, struct hisi_clock_data *data)
2140aa0c95fSHaojian Zhuang {
2150aa0c95fSHaojian Zhuang 	struct clk *clk;
21675af25f5SHaojian Zhuang 	void __iomem *base = data->base;
2170aa0c95fSHaojian Zhuang 	int i;
2180aa0c95fSHaojian Zhuang 
2190aa0c95fSHaojian Zhuang 	for (i = 0; i < nums; i++) {
2200aa0c95fSHaojian Zhuang 		clk = clk_register_divider_table(NULL, clks[i].name,
2210aa0c95fSHaojian Zhuang 						 clks[i].parent_name,
2220aa0c95fSHaojian Zhuang 						 clks[i].flags,
2230aa0c95fSHaojian Zhuang 						 base + clks[i].offset,
2240aa0c95fSHaojian Zhuang 						 clks[i].shift, clks[i].width,
2250aa0c95fSHaojian Zhuang 						 clks[i].div_flags,
2260aa0c95fSHaojian Zhuang 						 clks[i].table,
2270aa0c95fSHaojian Zhuang 						 &hisi_clk_lock);
2280aa0c95fSHaojian Zhuang 		if (IS_ERR(clk)) {
2290aa0c95fSHaojian Zhuang 			pr_err("%s: failed to register clock %s\n",
2300aa0c95fSHaojian Zhuang 			       __func__, clks[i].name);
2315497f668SJiancheng Xue 			goto err;
2320aa0c95fSHaojian Zhuang 		}
2330aa0c95fSHaojian Zhuang 
2340aa0c95fSHaojian Zhuang 		if (clks[i].alias)
2350aa0c95fSHaojian Zhuang 			clk_register_clkdev(clk, clks[i].alias, NULL);
2360aa0c95fSHaojian Zhuang 
23775af25f5SHaojian Zhuang 		data->clk_data.clks[clks[i].id] = clk;
2380aa0c95fSHaojian Zhuang 	}
2395497f668SJiancheng Xue 
2405497f668SJiancheng Xue 	return 0;
2415497f668SJiancheng Xue 
2425497f668SJiancheng Xue err:
2435497f668SJiancheng Xue 	while (i--)
2445497f668SJiancheng Xue 		clk_unregister_divider(data->clk_data.clks[clks[i].id]);
2455497f668SJiancheng Xue 
2465497f668SJiancheng Xue 	return PTR_ERR(clk);
2470aa0c95fSHaojian Zhuang }
248f6ff57c8SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_register_divider);
2490aa0c95fSHaojian Zhuang 
hisi_clk_register_gate(const struct hisi_gate_clock * clks,int nums,struct hisi_clock_data * data)2505497f668SJiancheng Xue int hisi_clk_register_gate(const struct hisi_gate_clock *clks,
2518b9dcb6cSZhangfei Gao 				       int nums, struct hisi_clock_data *data)
2528b9dcb6cSZhangfei Gao {
2538b9dcb6cSZhangfei Gao 	struct clk *clk;
2548b9dcb6cSZhangfei Gao 	void __iomem *base = data->base;
2558b9dcb6cSZhangfei Gao 	int i;
2568b9dcb6cSZhangfei Gao 
2578b9dcb6cSZhangfei Gao 	for (i = 0; i < nums; i++) {
2588b9dcb6cSZhangfei Gao 		clk = clk_register_gate(NULL, clks[i].name,
2598b9dcb6cSZhangfei Gao 						clks[i].parent_name,
2608b9dcb6cSZhangfei Gao 						clks[i].flags,
2618b9dcb6cSZhangfei Gao 						base + clks[i].offset,
2628b9dcb6cSZhangfei Gao 						clks[i].bit_idx,
2638b9dcb6cSZhangfei Gao 						clks[i].gate_flags,
2648b9dcb6cSZhangfei Gao 						&hisi_clk_lock);
2658b9dcb6cSZhangfei Gao 		if (IS_ERR(clk)) {
2668b9dcb6cSZhangfei Gao 			pr_err("%s: failed to register clock %s\n",
2678b9dcb6cSZhangfei Gao 			       __func__, clks[i].name);
2685497f668SJiancheng Xue 			goto err;
2698b9dcb6cSZhangfei Gao 		}
2708b9dcb6cSZhangfei Gao 
2718b9dcb6cSZhangfei Gao 		if (clks[i].alias)
2728b9dcb6cSZhangfei Gao 			clk_register_clkdev(clk, clks[i].alias, NULL);
2738b9dcb6cSZhangfei Gao 
2748b9dcb6cSZhangfei Gao 		data->clk_data.clks[clks[i].id] = clk;
2758b9dcb6cSZhangfei Gao 	}
2765497f668SJiancheng Xue 
2775497f668SJiancheng Xue 	return 0;
2785497f668SJiancheng Xue 
2795497f668SJiancheng Xue err:
2805497f668SJiancheng Xue 	while (i--)
2815497f668SJiancheng Xue 		clk_unregister_gate(data->clk_data.clks[clks[i].id]);
2825497f668SJiancheng Xue 
2835497f668SJiancheng Xue 	return PTR_ERR(clk);
2848b9dcb6cSZhangfei Gao }
285f6ff57c8SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_register_gate);
2868b9dcb6cSZhangfei Gao 
hisi_clk_register_gate_sep(const struct hisi_gate_clock * clks,int nums,struct hisi_clock_data * data)287f6ff57c8SJiancheng Xue void hisi_clk_register_gate_sep(const struct hisi_gate_clock *clks,
28875af25f5SHaojian Zhuang 				       int nums, struct hisi_clock_data *data)
2890aa0c95fSHaojian Zhuang {
2900aa0c95fSHaojian Zhuang 	struct clk *clk;
29175af25f5SHaojian Zhuang 	void __iomem *base = data->base;
2920aa0c95fSHaojian Zhuang 	int i;
2930aa0c95fSHaojian Zhuang 
2940aa0c95fSHaojian Zhuang 	for (i = 0; i < nums; i++) {
2950aa0c95fSHaojian Zhuang 		clk = hisi_register_clkgate_sep(NULL, clks[i].name,
2960aa0c95fSHaojian Zhuang 						clks[i].parent_name,
2970aa0c95fSHaojian Zhuang 						clks[i].flags,
2980aa0c95fSHaojian Zhuang 						base + clks[i].offset,
2990aa0c95fSHaojian Zhuang 						clks[i].bit_idx,
3000aa0c95fSHaojian Zhuang 						clks[i].gate_flags,
3010aa0c95fSHaojian Zhuang 						&hisi_clk_lock);
3020aa0c95fSHaojian Zhuang 		if (IS_ERR(clk)) {
3030aa0c95fSHaojian Zhuang 			pr_err("%s: failed to register clock %s\n",
3040aa0c95fSHaojian Zhuang 			       __func__, clks[i].name);
3050aa0c95fSHaojian Zhuang 			continue;
3060aa0c95fSHaojian Zhuang 		}
3070aa0c95fSHaojian Zhuang 
3080aa0c95fSHaojian Zhuang 		if (clks[i].alias)
3090aa0c95fSHaojian Zhuang 			clk_register_clkdev(clk, clks[i].alias, NULL);
3100aa0c95fSHaojian Zhuang 
31175af25f5SHaojian Zhuang 		data->clk_data.clks[clks[i].id] = clk;
3120aa0c95fSHaojian Zhuang 	}
3130aa0c95fSHaojian Zhuang }
314f6ff57c8SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_register_gate_sep);
31572ea4861SBintian Wang 
hi6220_clk_register_divider(const struct hi6220_divider_clock * clks,int nums,struct hisi_clock_data * data)316f6ff57c8SJiancheng Xue void __init hi6220_clk_register_divider(const struct hi6220_divider_clock *clks,
31772ea4861SBintian Wang 					int nums, struct hisi_clock_data *data)
31872ea4861SBintian Wang {
31972ea4861SBintian Wang 	struct clk *clk;
32072ea4861SBintian Wang 	void __iomem *base = data->base;
32172ea4861SBintian Wang 	int i;
32272ea4861SBintian Wang 
32372ea4861SBintian Wang 	for (i = 0; i < nums; i++) {
32472ea4861SBintian Wang 		clk = hi6220_register_clkdiv(NULL, clks[i].name,
32572ea4861SBintian Wang 						clks[i].parent_name,
32672ea4861SBintian Wang 						clks[i].flags,
32772ea4861SBintian Wang 						base + clks[i].offset,
32872ea4861SBintian Wang 						clks[i].shift,
32972ea4861SBintian Wang 						clks[i].width,
33072ea4861SBintian Wang 						clks[i].mask_bit,
33172ea4861SBintian Wang 						&hisi_clk_lock);
33272ea4861SBintian Wang 		if (IS_ERR(clk)) {
33372ea4861SBintian Wang 			pr_err("%s: failed to register clock %s\n",
33472ea4861SBintian Wang 			       __func__, clks[i].name);
33572ea4861SBintian Wang 			continue;
33672ea4861SBintian Wang 		}
33772ea4861SBintian Wang 
33872ea4861SBintian Wang 		if (clks[i].alias)
33972ea4861SBintian Wang 			clk_register_clkdev(clk, clks[i].alias, NULL);
34072ea4861SBintian Wang 
34172ea4861SBintian Wang 		data->clk_data.clks[clks[i].id] = clk;
34272ea4861SBintian Wang 	}
34372ea4861SBintian Wang }
344