1/* 2 * Blackfin Pulse Width Modulation (PWM) core 3 * 4 * Copyright (c) 2011 Analog Devices Inc. 5 * 6 * Licensed under the GPL-2 or later. 7 */ 8 9#include <linux/module.h> 10#include <linux/platform_device.h> 11#include <linux/pwm.h> 12#include <linux/slab.h> 13 14#include <asm/gptimers.h> 15#include <asm/portmux.h> 16 17struct bfin_pwm_chip { 18 struct pwm_chip chip; 19}; 20 21struct bfin_pwm { 22 unsigned short pin; 23}; 24 25static const unsigned short pwm_to_gptimer_per[] = { 26 P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR4, P_TMR5, 27 P_TMR6, P_TMR7, P_TMR8, P_TMR9, P_TMR10, P_TMR11, 28}; 29 30static int bfin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 31{ 32 struct bfin_pwm *priv; 33 int ret; 34 35 if (pwm->hwpwm >= ARRAY_SIZE(pwm_to_gptimer_per)) 36 return -EINVAL; 37 38 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 39 if (!priv) 40 return -ENOMEM; 41 42 priv->pin = pwm_to_gptimer_per[pwm->hwpwm]; 43 44 ret = peripheral_request(priv->pin, NULL); 45 if (ret) { 46 kfree(priv); 47 return ret; 48 } 49 50 pwm_set_chip_data(pwm, priv); 51 52 return 0; 53} 54 55static void bfin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) 56{ 57 struct bfin_pwm *priv = pwm_get_chip_data(pwm); 58 59 if (priv) { 60 peripheral_free(priv->pin); 61 kfree(priv); 62 } 63} 64 65static int bfin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 66 int duty_ns, int period_ns) 67{ 68 struct bfin_pwm *priv = pwm_get_chip_data(pwm); 69 unsigned long period, duty; 70 unsigned long long val; 71 72 val = (unsigned long long)get_sclk() * period_ns; 73 do_div(val, NSEC_PER_SEC); 74 period = val; 75 76 val = (unsigned long long)period * duty_ns; 77 do_div(val, period_ns); 78 duty = period - val; 79 80 if (duty >= period) 81 duty = period - 1; 82 83 set_gptimer_config(priv->pin, TIMER_MODE_PWM | TIMER_PERIOD_CNT); 84 set_gptimer_pwidth(priv->pin, duty); 85 set_gptimer_period(priv->pin, period); 86 87 return 0; 88} 89 90static int bfin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 91{ 92 struct bfin_pwm *priv = pwm_get_chip_data(pwm); 93 94 enable_gptimer(priv->pin); 95 96 return 0; 97} 98 99static void bfin_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 100{ 101 struct bfin_pwm *priv = pwm_get_chip_data(pwm); 102 103 disable_gptimer(priv->pin); 104} 105 106static struct pwm_ops bfin_pwm_ops = { 107 .request = bfin_pwm_request, 108 .free = bfin_pwm_free, 109 .config = bfin_pwm_config, 110 .enable = bfin_pwm_enable, 111 .disable = bfin_pwm_disable, 112 .owner = THIS_MODULE, 113}; 114 115static int bfin_pwm_probe(struct platform_device *pdev) 116{ 117 struct bfin_pwm_chip *pwm; 118 int ret; 119 120 pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); 121 if (!pwm) { 122 dev_err(&pdev->dev, "failed to allocate memory\n"); 123 return -ENOMEM; 124 } 125 126 platform_set_drvdata(pdev, pwm); 127 128 pwm->chip.dev = &pdev->dev; 129 pwm->chip.ops = &bfin_pwm_ops; 130 pwm->chip.base = -1; 131 pwm->chip.npwm = 12; 132 133 ret = pwmchip_add(&pwm->chip); 134 if (ret < 0) { 135 dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); 136 return ret; 137 } 138 139 return 0; 140} 141 142static int bfin_pwm_remove(struct platform_device *pdev) 143{ 144 struct bfin_pwm_chip *pwm = platform_get_drvdata(pdev); 145 146 return pwmchip_remove(&pwm->chip); 147} 148 149static struct platform_driver bfin_pwm_driver = { 150 .driver = { 151 .name = "bfin-pwm", 152 .owner = THIS_MODULE, 153 }, 154 .probe = bfin_pwm_probe, 155 .remove = bfin_pwm_remove, 156}; 157 158module_platform_driver(bfin_pwm_driver); 159 160MODULE_LICENSE("GPL"); 161