Example #1
0
/*
 * _ti814x_adpll_check_and_set_rate - check if given clock's parent is an ADPLL
 *  and check if it is allowed to change the rate of the ADPLL based on id,
 *  Set rate if it is a clock not an ADPLL and propagate rate down tree
 * @clk - pointer to struct clk
 * @dpll_setrate - output variable indicating whether rate can be set
 * @rate - rate to be set
 */
static int _ti814x_adpll_check_and_set_rate(struct clk *clk,
					int *dpll_setrate,
					unsigned long rate)
{
	struct dpll_data *dd;
	struct clk *dclk = clk->parent;
	int ret = 0;

	*dpll_setrate = 0;

	if (dclk->dpll_data) {
		dd = dclk->dpll_data;
		if (((dd->dpll_id == TI814X_ARM_DPLL_ID) ||
				(dd->dpll_id == TI814X_DDR_DPLL_ID)))
			return -EINVAL;
		*dpll_setrate = 1;
	} else if (dclk->set_rate) {
		if (dclk->usecount > 1) {
			pr_warn("clock: %s,\'s parent %s is already in use, can't"
				" change rate\n", clk->name, dclk->name);
			return -EBUSY; //?EWB
		}
		ret = dclk->set_rate(dclk, rate);
		if (!ret) {
			/* re-set divider based on parent rate */
			ret = omap2_clksel_set_rate(clk, dclk->rate);
			if (ret) {
				pr_err("clock: failed to reset the divider\n");
				return ret;
			}
			propagate_rate(dclk);
		}
	}
	return ret;
}
Example #2
0
/**
 * ti814x_clksel_set_rate() - program clock rate in hardware
 * @clk: struct clk * to program rate
 * @rate: target rate to program
 *
 * This function is intended to be called by the audio and some
 * of the coprocessors. Program @clk's rate to @rate in the hardware.
 * The clock must be enabled, or -EPERM is returned
 * If multiple drivers are using the clock, returns -EBUSY with error message.
 * Returns -EINVAL upon error, or 0 upon success.
 */
int ti814x_clksel_set_rate(struct clk *clk, unsigned long rate)
{
	struct clk *pclk;
	struct clk *dclk;
	int ret, dpll_setrate = 0;

	if (clk->usecount == 0) {
		pr_err("clock: Enable %s before setting rate\n",
			clk->name);
		return -EPERM;
	}
	if (clk->usecount > 1) {
		pr_warn("clock: %s usecount(%d) > 1 - can't change the rate\n",
			clk->name, clk->usecount);
		return -EBUSY; //?EWB
	}

	pclk = clk->parent;
	if (pclk->usecount > 1) {
		pr_warn("clock: %s's parent %s usecount(%d) > 1 -"
			"can't change the rate\n", clk->name,
			pclk->name, pclk->usecount);
		return -EBUSY; //?EWB
	}

	dclk = pclk->parent;
	/* check the dividers in parent clock */
	ret = pclk->set_rate(pclk, rate);
	if (!ret) {
		propagate_rate(pclk);
	} else {

		ret = _ti814x_adpll_check_and_set_rate(pclk,
						&dpll_setrate,
						rate);

		if (ret && dpll_setrate)
			goto failed_set_divider;
		else if (ret) {
			pclk = dclk;
			dclk = dclk->parent;
			ret = _ti814x_adpll_check_and_set_rate(pclk,
							&dpll_setrate,
							rate);
			if (ret && dpll_setrate)
				goto failed_set_divider;
		}
	}
	if (dpll_setrate) {
		if (dclk->usecount > 1) {
			pr_warn("clock: %s's parent %s usecount(%d) > 1"
				" - can't change the rate\n", pclk->name,
				dclk->name, dclk->usecount);
			return -EBUSY; //?EWB
		}

		/* Changing the DPLL rate */
		ret = dclk->set_rate(dclk, rate);
		if (ret) {
			pr_err("clock: failed to set dpll rate\n");
			return -EINVAL;
		}

		/* reset divider */
		ret = omap2_clksel_set_rate(pclk, dclk->rate);
		if (ret) {
			pr_err("clock: failed to reset the divider\n");
			return -EINVAL;
		}
		propagate_rate(dclk);
	}
	return 0;

failed_set_divider:
	pr_err("clock: failed to set divider\n");
	return ret;
}
Example #3
0
/**
 * ti816x_clksel_set_rate() - program clock rate in hardware
 * @clk: struct clk * to program rate
 * @rate: target rate to program
 *
 * This function is intended to be called by the audio and some
 * of the coprocessors. Program @clk's rate to @rate in the hardware.
 * The clock should be disabled when this happens, otherwise setrate
 * will fail, because this is treated as clock being used. If multiple
 * drivers are using the clock, even though it is trying to change
 * then this return -EINVAL with error message. Returns -EINVAL upon
 * error, or 0 upon success.
 */
int ti816x_clksel_set_rate(struct clk *clk, unsigned long rate)
{
	struct clk *pclk;
	struct clk *fclk;
	struct fapll_data *fd;
	int ret, fapll_sr = 0;

	if (clk->usecount == 0) {
		pr_err("clock: Enable the clock '%s' before setting rate\n",
			clk->name);
		return -EINVAL;
	}
	if (clk->usecount > 1) {
		pr_err("clock: '%s' clock is in use can't change the rate "
			"usecount = '%d'", clk->name, clk->usecount);
		return -EBUSY;
	}

	pclk = clk->parent;
	if (pclk->usecount > 1) {
		pr_err("clock: '%s' clock's parent '%s' is in use can't"
			" change the rate usecount = %d", clk->name,
			pclk->name, pclk->usecount);
		return -EBUSY;
	}

	fclk = pclk->parent;
	/* check the dividers in parent clock */
	ret = pclk->set_rate(pclk, rate);
	if (!ret)
		propagate_rate(pclk);
	else {
		if (!fclk->fapll_data)
			goto failed_set_divider;

		fd = fclk->fapll_data;
		if (!((fd->fapll_id == 1) || (fd->fapll_id == 4)))
			goto failed_set_divider;

		if ((fd->fapll_id == 1) && (fclk->synthesizer_id == 2))
			goto failed_set_divider;

		fapll_sr = 1;
	}

	if (fapll_sr) {
		if (fclk->usecount > 1) {
			pr_err("clock: %s, parent %s is already in use, change"
				" parent\n", pclk->name, pclk->parent->name);
			return -EINVAL;
		}

		/* Changing the FAPLL synthesizer rate */
		ret = fclk->set_rate(fclk, rate);
		if (ret) {
			pr_err("clock: failed to set fapll rate\n");
			return -EINVAL;
		}

		/* reset divider */
		ret = omap2_clksel_set_rate(pclk, fclk->rate);
		if (ret) {
			pr_err("clock: failed to reset the divider\n");
			return -EINVAL;
		}
		propagate_rate(fclk);
	}
	return 0;

failed_set_divider:
	pr_err("clock: failed to set divider\n");
	return -EINVAL;
}
Example #4
0
int __init archos_audio_gpio_init(void)
{
	const struct archos_audio_config *audio_cfg;
	struct clk *clkout2_src_ck;
	struct clk *sys_clkout2;
	struct clk *core_ck;

	/* audio  */
	audio_cfg = omap_get_config( ARCHOS_TAG_AUDIO, struct archos_audio_config );
	if (audio_cfg == NULL) {
		pr_err("archos_audio_gpio_init: no board configuration found\n");
		return -ENODEV;
	}
	if ( hardware_rev >= audio_cfg->nrev ) {
		pr_err("archos_audio_gpio_init: hardware_rev (%i) >= nrev (%i)\n",
			hardware_rev, audio_cfg->nrev);
		return -ENODEV;
	}

	audio_gpio = audio_cfg->rev[hardware_rev];

	// a32 & a43 protos where using clkout1.
	if (hardware_rev >= 1 || !(machine_is_archos_a32() || machine_is_archos_a43())) {
		core_ck = clk_get(NULL, "cm_96m_fck");
		if (IS_ERR(core_ck)) {
			printk(KERN_ERR "failed to get core_ck\n");
		}
	
		clkout2_src_ck = clk_get(NULL, "clkout2_src_ck");
		if (IS_ERR(clkout2_src_ck)) {
			printk(KERN_ERR "failed to get clkout2_src_ck\n");
		}
	
		sys_clkout2 = clk_get(NULL, "sys_clkout2");
		if (IS_ERR(sys_clkout2)) {
			printk(KERN_ERR "failed to get sys_clkout2\n");
		}
	
		if ( clk_set_parent(clkout2_src_ck, core_ck) != 0) {
			printk(KERN_ERR "failed to set sys_clkout2 parent to clkout2\n");
		}
	
		/* Set the clock to 12 Mhz */
		omap2_clksel_set_rate(sys_clkout2, 12000000);

		clk_put(sys_clkout2);
		clk_put(clkout2_src_ck);
		clk_put(core_ck);

		sysclock_name = sys_clkout2_name;
		use_mcbsp1_fclk = 1;
	} else {
		sysclock_name = sys_clkout1_name;
	}

	if (GPIO_PIN( audio_gpio.spdif ) != -1)
		archos_gpio_init_output( &audio_gpio.spdif, "spdif" );

	if (GPIO_PIN( audio_gpio.hp_on ) != -1)
		archos_gpio_init_output( &audio_gpio.hp_on, "hp_on" );

	if (GPIO_PIN( audio_gpio.headphone_plugged ) != -1)
		archos_gpio_init_input( &audio_gpio.headphone_plugged, "hp_detect" );

	if (GPIO_PIN( audio_gpio.vamp_vbat ) != -1)
		archos_gpio_init_output( &audio_gpio.vamp_vbat, "vamp_vbat" );
	if (GPIO_PIN( audio_gpio.vamp_dc ) != -1)
		archos_gpio_init_output( &audio_gpio.vamp_dc, "vamp_dc" );

	// XXX maybe prevents OFF mode?
	if (GPIO_PIN( audio_gpio.headphone_plugged ) != -1)
		gpio_set_debounce(GPIO_PIN(audio_gpio.headphone_plugged), (1 + 1) * 0x1f);

	if (GPIO_PIN(audio_gpio.vamp_dc) != -1) {
		int ret = platform_device_register(&archos_audio_vamp_device);
		if (ret < 0)
			return ret;

		ret = platform_driver_register(&archos_audio_vamp_driver);
		if (ret < 0)
			return ret;

		ret = device_create_file(&archos_audio_vamp_device.dev, &dev_attr_vamp_vusb_ctrl);
		if (ret < 0)
			return ret;

		hrtimer_init(&vamp_watchdog_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
		vamp_watchdog_timer.function = vamp_watchdog_timer_func;
	}

	pr_debug("%s init done\n", __FUNCTION__);
	return 0;
}