static struct alpha_pll_clk *alpha_pll_dt_parser(struct device *dev,
						struct device_node *np)
{
	struct alpha_pll_clk *pll;
	struct msmclk_data *drv;

	pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
	if (!pll) {
		dt_err(np, "memory alloc failure\n");
		return ERR_PTR(-ENOMEM);
	}

	if (of_property_read_u32(np, "qcom,base-offset", &pll->offset)) {
		dt_err(np, "missing qcom,base-offset\n");
		return ERR_PTR(-EINVAL);
	}

	/* Optional property */
	of_property_read_u32(np, "qcom,post-div-config",
					&pll->post_div_config);

	pll->masks = devm_kzalloc(dev, sizeof(*pll->masks), GFP_KERNEL);
	if (!pll->masks) {
		dt_err(np, "memory alloc failure\n");
		return ERR_PTR(-ENOMEM);
	}

	if (of_device_is_compatible(np, "qcom,fixed-alpha-pll-20p") ||
		of_device_is_compatible(np, "qcom,alpha-pll-20p")) {
		*pll->masks = masks_20nm_p;
		pll->vco_tbl = vco_20nm_p;
		pll->num_vco = ARRAY_SIZE(vco_20nm_p);
	} else if (of_device_is_compatible(np, "qcom,fixed-alpha-pll-20t") ||
		of_device_is_compatible(np, "qcom,alpha-pll-20t")) {
		*pll->masks = masks_20nm_t;
		pll->vco_tbl = vco_20nm_t;
		pll->num_vco = ARRAY_SIZE(vco_20nm_t);
	} else {
		dt_err(np, "unexpected compatible string\n");
		return ERR_PTR(-EINVAL);
	}

	drv = msmclk_parse_phandle(dev, np->parent->phandle);
	if (IS_ERR_OR_NULL(drv))
		return ERR_CAST(drv);
	pll->base = &drv->base;
	return pll;
}
static void *fixed_rate_alpha_pll_dt_parser(struct device *dev,
						struct device_node *np)
{
	struct alpha_pll_clk *pll;
	int rc;
	u32 val;

	pll = alpha_pll_dt_parser(dev, np);
	if (IS_ERR(pll))
		return pll;

	rc = of_property_read_u32(np, "qcom,pll-config-rate", &val);
	if (rc) {
		dt_err(np, "missing qcom,pll-config-rate\n");
		return ERR_PTR(-EINVAL);
	}
	pll->c.rate = val;

	rc = of_property_read_u32(np, "qcom,output-enable",
						&pll->enable_config);
	if (rc) {
		dt_err(np, "missing qcom,output-enable\n");
		return ERR_PTR(-EINVAL);
	}

	/* Optional Property */
	rc = of_property_read_u32(np, "qcom,fsm-en-bit", &val);
	if (!rc) {
		rc = of_property_read_u32(np, "qcom,fsm-en-offset",
						&pll->fsm_reg_offset);
		if (rc) {
			dt_err(np, "missing qcom,fsm-en-offset\n");
			return ERR_PTR(-EINVAL);
		}
		pll->fsm_en_mask = BIT(val);
	}

	pll->c.ops = &clk_ops_fixed_alpha_pll;
	return msmclk_generic_clk_init(dev, np, &pll->c);
}
static void *votable_pll_clk_dt_parser(struct device *dev,
						struct device_node *np)
{
	struct pll_vote_clk *v, *peer;
	struct clk *c;
	u32 val, rc;
	phandle p;
	struct msmclk_data *drv;

	v = devm_kzalloc(dev, sizeof(*v), GFP_KERNEL);
	if (!v) {
		dt_err(np, "memory alloc failure\n");
		return ERR_PTR(-ENOMEM);
	}

	drv = msmclk_parse_phandle(dev, np->parent->phandle);
	if (IS_ERR_OR_NULL(drv))
		return ERR_CAST(drv);
	v->base = &drv->base;

	rc = of_property_read_u32(np, "qcom,en-offset", (u32 *)&v->en_reg);
	if (rc) {
		dt_err(np, "missing qcom,en-offset dt property\n");
		return ERR_PTR(-EINVAL);
	}

	rc = of_property_read_u32(np, "qcom,en-bit", &val);
	if (rc) {
		dt_err(np, "missing qcom,en-bit dt property\n");
		return ERR_PTR(-EINVAL);
	}
	v->en_mask = BIT(val);

	rc = of_property_read_u32(np, "qcom,status-offset",
						(u32 *)&v->status_reg);
	if (rc) {
		dt_err(np, "missing qcom,status-offset dt property\n");
		return ERR_PTR(-EINVAL);
	}

	rc = of_property_read_u32(np, "qcom,status-bit", &val);
	if (rc) {
		dt_err(np, "missing qcom,status-bit dt property\n");
		return ERR_PTR(-EINVAL);
	}
	v->status_mask = BIT(val);

	rc = of_property_read_u32(np, "qcom,pll-config-rate", &val);
	if (rc) {
		dt_err(np, "missing qcom,pll-config-rate dt property\n");
		return ERR_PTR(-EINVAL);
	}
	v->c.rate = val;

	if (of_device_is_compatible(np, "qcom,active-only-pll"))
		v->soft_vote_mask = PLL_SOFT_VOTE_ACPU;
	else if (of_device_is_compatible(np, "qcom,sleep-active-pll"))
		v->soft_vote_mask = PLL_SOFT_VOTE_PRIMARY;

	if (of_device_is_compatible(np, "qcom,votable-pll")) {
		v->c.ops = &clk_ops_pll_vote;
		return msmclk_generic_clk_init(dev, np, &v->c);
	}

	rc = of_property_read_phandle_index(np, "qcom,peer", 0, &p);
	if (rc) {
		dt_err(np, "missing qcom,peer dt property\n");
		return ERR_PTR(-EINVAL);
	}

	c = msmclk_lookup_phandle(dev, p);
	if (!IS_ERR_OR_NULL(c)) {
		v->soft_vote = devm_kzalloc(dev, sizeof(*v->soft_vote),
						GFP_KERNEL);
		if (!v->soft_vote) {
			dt_err(np, "memory alloc failure\n");
			return ERR_PTR(-ENOMEM);
		}

		peer = to_pll_vote_clk(c);
		peer->soft_vote = v->soft_vote;
	}

	v->c.ops = &clk_ops_pll_acpu_vote;
	return msmclk_generic_clk_init(dev, np, &v->c);
}
Пример #4
0
int enable_rpm_scaling(void)
{
	int rc, value = 0x1;
	static int is_inited;

	struct msm_rpm_kvp kvp = {
		.key = RPM_SMD_KEY_ENABLE,
		.data = (void *)&value,
		.length = sizeof(value),
	};

	if (is_inited)
		return 0;

	rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_SLEEP_SET,
			RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
	if (rc < 0) {
		if (rc != -EPROBE_DEFER)
			WARN(1, "RPM clock scaling (sleep set) did not enable!\n");
		return rc;
	}

	rc = msm_rpm_send_message_noirq(MSM_RPM_CTX_ACTIVE_SET,
			RPM_MISC_CLK_TYPE, RPM_SCALING_ENABLE_ID, &kvp, 1);
	if (rc < 0) {
		if (rc != -EPROBE_DEFER)
			WARN(1, "RPM clock scaling (active set) did not enable!\n");
		return rc;
	}

	is_inited++;
	return 0;
}

struct clk_ops clk_ops_rpm = {
	.prepare = rpm_clk_prepare,
	.unprepare = rpm_clk_unprepare,
	.set_rate = rpm_clk_set_rate,
	.get_rate = rpm_clk_get_rate,
	.is_enabled = rpm_clk_is_enabled,
	.round_rate = rpm_clk_round_rate,
	.is_local = rpm_clk_is_local,
	.handoff = rpm_clk_handoff,
};

struct clk_ops clk_ops_rpm_branch = {
	.prepare = rpm_clk_prepare,
	.unprepare = rpm_clk_unprepare,
	.is_local = rpm_clk_is_local,
	.handoff = rpm_clk_handoff,
};

static struct rpm_clk *rpm_clk_dt_parser_common(struct device *dev,
						struct device_node *np)
{
	struct rpm_clk *rpm, *peer;
	struct clk *c;
	int rc = 0;
	phandle p;
	const char *str;

	rpm = devm_kzalloc(dev, sizeof(*rpm), GFP_KERNEL);
	if (!rpm) {
		dt_err(np, "memory alloc failure\n");
		return ERR_PTR(-ENOMEM);
	}

	rc = of_property_read_phandle_index(np, "qcom,rpm-peer", 0, &p);
	if (rc) {
		dt_err(np, "missing qcom,rpm-peer dt property\n");
		return ERR_PTR(rc);
	}

	/* Rely on whoever's called last to setup the circular ref */
	c = msmclk_lookup_phandle(dev, p);
	if (!IS_ERR(c)) {
		peer = to_rpm_clk(c);
		peer->peer = rpm;
		rpm->peer = peer;
	}

	rpm->rpmrs_data = &clk_rpmrs_data_smd;
	rpm->active_only = of_device_is_compatible(np, "qcom,rpm-a-clk") ||
			of_device_is_compatible(np, "qcom,rpm-branch-a-clk");

	rc = of_property_read_string(np, "qcom,res-type", &str);
	if (rc) {
		dt_err(np, "missing qcom,res-type dt property\n");
		return ERR_PTR(rc);
	}
	sscanf(str, "%4c", (char *) &rpm->rpm_res_type);

	rc = of_property_read_u32(np, "qcom,res-id", &rpm->rpm_clk_id);
	if (rc) {
		dt_err(np, "missing qcom,res-id dt property\n");
		return ERR_PTR(rc);
	}

	rc = of_property_read_string(np, "qcom,key", &str);
	if (rc) {
		dt_err(np, "missing qcom,key dt property\n");
		return ERR_PTR(rc);
	}
	sscanf(str, "%4c", (char *) &rpm->rpm_key);
	return rpm;
}

static void *rpm_clk_dt_parser(struct device *dev, struct device_node *np)
{
	struct rpm_clk *rpm;
	rpm = rpm_clk_dt_parser_common(dev, np);
	if (IS_ERR(rpm))
		return rpm;

	rpm->c.ops = &clk_ops_rpm;
	return msmclk_generic_clk_init(dev, np, &rpm->c);
}

static void *rpm_branch_clk_dt_parser(struct device *dev,
					struct device_node *np)
{
	struct rpm_clk *rpm;
	u32 rate;
	int rc;
	rpm = rpm_clk_dt_parser_common(dev, np);
	if (IS_ERR(rpm))
		return rpm;

	rpm->c.ops = &clk_ops_rpm_branch;
	rpm->branch = true;

	rc = of_property_read_u32(np, "qcom,rcg-init-rate", &rate);
	if (!rc)
		rpm->c.rate = rate;

	return msmclk_generic_clk_init(dev, np, &rpm->c);
}
MSMCLK_PARSER(rpm_clk_dt_parser, "qcom,rpm-clk", 0);
MSMCLK_PARSER(rpm_clk_dt_parser, "qcom,rpm-a-clk", 1);
MSMCLK_PARSER(rpm_branch_clk_dt_parser, "qcom,rpm-branch-clk", 0);
MSMCLK_PARSER(rpm_branch_clk_dt_parser, "qcom,rpm-branch-a-clk", 1);