int pwm_channel_alloc(int index, struct pwm_channel *ch) { unsigned long flags; int status = 0; /* insist on PWM init, with this signal pinned out */ if (!pwm || !(pwm->mask & 1 << index)) return -ENODEV; if (index < 0 || index >= PWM_NCHAN || !ch) return -EINVAL; memset(ch, 0, sizeof *ch); spin_lock_irqsave(&pwm->lock, flags); if (pwm->channel[index]) status = -EBUSY; else { clk_enable(pwm->clk); ch->regs = pwmc_regs(pwm, index); ch->index = index; /* REVISIT: ap7000 seems to go 2x as fast as we expect!! */ ch->mck = clk_get_rate(pwm->clk); pwm->channel[index] = ch; pwm->handler[index] = NULL; /* channel and irq are always disabled when we return */ pwm_writel(pwm, PWM_DIS, 1 << index); pwm_writel(pwm, PWM_IDR, 1 << index); } spin_unlock_irqrestore(&pwm->lock, flags); return status; }
int pwm_channel_alloc(int index, struct pwm_channel *ch) { unsigned long flags; int status = 0; if (!pwm || !(pwm->mask & 1 << index)) return -ENODEV; if (index < 0 || index >= PWM_NCHAN || !ch) return -EINVAL; memset(ch, 0, sizeof *ch); spin_lock_irqsave(&pwm->lock, flags); if (pwm->channel[index]) status = -EBUSY; else { clk_enable(pwm->clk); ch->regs = pwmc_regs(pwm, index); ch->index = index; ch->mck = clk_get_rate(pwm->clk); pwm->channel[index] = ch; pwm->handler[index] = NULL; pwm_writel(pwm, PWM_DIS, 1 << index); pwm_writel(pwm, PWM_IDR, 1 << index); } spin_unlock_irqrestore(&pwm->lock, flags); return status; }
/** * pwm_clk_free - deconfigure and release CLKA or CLKB * * Reverses the effect of pwm_clk_alloc(). */ void pwm_clk_free(unsigned clk) { unsigned long flags; u32 mr; spin_lock_irqsave(&pwm->lock, flags); mr = pwm_readl(pwm, PWM_MR); if (clk == PWM_CPR_CLKA) pwm_writel(pwm, PWM_MR, mr & ~(0xffff << 0)); if (clk == PWM_CPR_CLKB) pwm_writel(pwm, PWM_MR, mr & ~(0xffff << 16)); spin_unlock_irqrestore(&pwm->lock, flags); }
static int __exit pwm_remove(struct platform_device *pdev) { struct pwm *p = platform_get_drvdata(pdev); if (p != pwm) return -EINVAL; clk_enable(pwm->clk); pwm_writel(pwm, PWM_DIS, (1 << PWM_NCHAN) - 1); pwm_writel(pwm, PWM_IDR, (1 << PWM_NCHAN) - 1); clk_disable(pwm->clk); pwm = NULL; free_irq(p->irq, p); clk_put(p->clk); iounmap(p->base); kfree(p); return 0; }
/** * pwm_channel_free - release a previously allocated channel * @ch: the channel being released * * The channel is completely shut down (counter and IRQ disabled), * and made available for re-use. Returns zero, or negative errno. */ int pwm_channel_free(struct pwm_channel *ch) { unsigned long flags; int t; spin_lock_irqsave(&pwm->lock, flags); t = pwmcheck(ch); if (t >= 0) { pwm->channel[t] = NULL; pwm->handler[t] = NULL; /* channel and irq are always disabled when we return */ pwm_writel(pwm, PWM_DIS, 1 << t); pwm_writel(pwm, PWM_IDR, 1 << t); clk_disable(pwm->clk); t = 0; } spin_unlock_irqrestore(&pwm->lock, flags); return t; }
/** * pwm_channel_handler - manage channel's IRQ handler * @ch: the channel * @handler: the handler to use, possibly NULL * * If the handler is non-null, the handler will be called after every * period of this PWM channel. If the handler is null, this channel * won't generate an IRQ. */ int pwm_channel_handler(struct pwm_channel *ch, void (*handler)(struct pwm_channel *ch)) { unsigned long flags; int t; spin_lock_irqsave(&pwm->lock, flags); t = pwmcheck(ch); if (t >= 0) { pwm->handler[t] = handler; pwm_writel(pwm, handler ? PWM_IER : PWM_IDR, 1 << t); t = 0; } spin_unlock_irqrestore(&pwm->lock, flags); return t; }
int __pwm_channel_onoff(struct pwm_channel *ch, int enabled) { unsigned long flags; int t; /* OMITTED FUNCTIONALITY: starting several channels in synch */ spin_lock_irqsave(&pwm->lock, flags); t = pwmcheck(ch); if (t >= 0) { pwm_writel(pwm, enabled ? PWM_ENA : PWM_DIS, 1 << t); t = 0; pwm_dumpregs(ch, enabled ? "enable" : "disable"); } spin_unlock_irqrestore(&pwm->lock, flags); return t; }
int __pwm_channel_onoff(struct pwm_channel *ch, int enabled) { unsigned long flags; int t; spin_lock_irqsave(&pwm->lock, flags); t = pwmcheck(ch); if (t >= 0) { pwm_writel(pwm, enabled ? PWM_ENA : PWM_DIS, 1 << t); t = 0; pwm_dumpregs(ch, enabled ? "enable" : "disable"); } spin_unlock_irqrestore(&pwm->lock, flags); return t; }
/** * pwm_clk_alloc - allocate and configure CLKA or CLKB * @prescale: from 0..10, the power of two used to divide MCK * @div: from 1..255, the linear divisor to use * * Returns PWM_CPR_CLKA, PWM_CPR_CLKB, or negative errno. The allocated * clock will run with a period of (2^prescale * div) / MCK, or twice as * long if center aligned PWM output is used. The clock must later be * deconfigured using pwm_clk_free(). */ int pwm_clk_alloc(unsigned prescale, unsigned div) { unsigned long flags; u32 mr; u32 val = (prescale << 8) | div; int ret = -EBUSY; if (prescale >= 10 || div == 0 || div > 255) return -EINVAL; spin_lock_irqsave(&pwm->lock, flags); mr = pwm_readl(pwm, PWM_MR); if ((mr & 0xffff) == 0) { mr |= val; ret = PWM_CPR_CLKA; } else if ((mr & (0xffff << 16)) == 0) { mr |= val << 16; ret = PWM_CPR_CLKB; } if (ret > 0) pwm_writel(pwm, PWM_MR, mr); spin_unlock_irqrestore(&pwm->lock, flags); return ret; }