/* * _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; }
/** * 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; }
/** * 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; }
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; }