Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.12 335 lines 8.4 kB view raw
1/* 2 * simple driver for PWM (Pulse Width Modulator) controller 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 * 8 * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com> 9 */ 10 11#include <linux/module.h> 12#include <linux/kernel.h> 13#include <linux/platform_device.h> 14#include <linux/slab.h> 15#include <linux/err.h> 16#include <linux/clk.h> 17#include <linux/delay.h> 18#include <linux/io.h> 19#include <linux/pwm.h> 20#include <linux/of.h> 21#include <linux/of_device.h> 22 23/* i.MX1 and i.MX21 share the same PWM function block: */ 24 25#define MX1_PWMC 0x00 /* PWM Control Register */ 26#define MX1_PWMS 0x04 /* PWM Sample Register */ 27#define MX1_PWMP 0x08 /* PWM Period Register */ 28 29#define MX1_PWMC_EN (1 << 4) 30 31/* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ 32 33#define MX3_PWMCR 0x00 /* PWM Control Register */ 34#define MX3_PWMSR 0x04 /* PWM Status Register */ 35#define MX3_PWMSAR 0x0C /* PWM Sample Register */ 36#define MX3_PWMPR 0x10 /* PWM Period Register */ 37#define MX3_PWMCR_PRESCALER(x) ((((x) - 1) & 0xFFF) << 4) 38#define MX3_PWMCR_DOZEEN (1 << 24) 39#define MX3_PWMCR_WAITEN (1 << 23) 40#define MX3_PWMCR_DBGEN (1 << 22) 41#define MX3_PWMCR_POUTC (1 << 18) 42#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) 43#define MX3_PWMCR_CLKSRC_IPG (1 << 16) 44#define MX3_PWMCR_SWR (1 << 3) 45#define MX3_PWMCR_EN (1 << 0) 46#define MX3_PWMSR_FIFOAV_4WORDS 0x4 47#define MX3_PWMSR_FIFOAV_MASK 0x7 48 49#define MX3_PWM_SWR_LOOP 5 50 51struct imx_chip { 52 struct clk *clk_per; 53 54 void __iomem *mmio_base; 55 56 struct pwm_chip chip; 57}; 58 59#define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) 60 61static int imx_pwm_config_v1(struct pwm_chip *chip, 62 struct pwm_device *pwm, int duty_ns, int period_ns) 63{ 64 struct imx_chip *imx = to_imx_chip(chip); 65 66 /* 67 * The PWM subsystem allows for exact frequencies. However, 68 * I cannot connect a scope on my device to the PWM line and 69 * thus cannot provide the program the PWM controller 70 * exactly. Instead, I'm relying on the fact that the 71 * Bootloader (u-boot or WinCE+haret) has programmed the PWM 72 * function group already. So I'll just modify the PWM sample 73 * register to follow the ratio of duty_ns vs. period_ns 74 * accordingly. 75 * 76 * This is good enough for programming the brightness of 77 * the LCD backlight. 78 * 79 * The real implementation would divide PERCLK[0] first by 80 * both the prescaler (/1 .. /128) and then by CLKSEL 81 * (/2 .. /16). 82 */ 83 u32 max = readl(imx->mmio_base + MX1_PWMP); 84 u32 p = max * duty_ns / period_ns; 85 writel(max - p, imx->mmio_base + MX1_PWMS); 86 87 return 0; 88} 89 90static int imx_pwm_enable_v1(struct pwm_chip *chip, struct pwm_device *pwm) 91{ 92 struct imx_chip *imx = to_imx_chip(chip); 93 u32 val; 94 int ret; 95 96 ret = clk_prepare_enable(imx->clk_per); 97 if (ret < 0) 98 return ret; 99 100 val = readl(imx->mmio_base + MX1_PWMC); 101 val |= MX1_PWMC_EN; 102 writel(val, imx->mmio_base + MX1_PWMC); 103 104 return 0; 105} 106 107static void imx_pwm_disable_v1(struct pwm_chip *chip, struct pwm_device *pwm) 108{ 109 struct imx_chip *imx = to_imx_chip(chip); 110 u32 val; 111 112 val = readl(imx->mmio_base + MX1_PWMC); 113 val &= ~MX1_PWMC_EN; 114 writel(val, imx->mmio_base + MX1_PWMC); 115 116 clk_disable_unprepare(imx->clk_per); 117} 118 119static void imx_pwm_sw_reset(struct pwm_chip *chip) 120{ 121 struct imx_chip *imx = to_imx_chip(chip); 122 struct device *dev = chip->dev; 123 int wait_count = 0; 124 u32 cr; 125 126 writel(MX3_PWMCR_SWR, imx->mmio_base + MX3_PWMCR); 127 do { 128 usleep_range(200, 1000); 129 cr = readl(imx->mmio_base + MX3_PWMCR); 130 } while ((cr & MX3_PWMCR_SWR) && 131 (wait_count++ < MX3_PWM_SWR_LOOP)); 132 133 if (cr & MX3_PWMCR_SWR) 134 dev_warn(dev, "software reset timeout\n"); 135} 136 137static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip, 138 struct pwm_device *pwm) 139{ 140 struct imx_chip *imx = to_imx_chip(chip); 141 struct device *dev = chip->dev; 142 unsigned int period_ms; 143 int fifoav; 144 u32 sr; 145 146 sr = readl(imx->mmio_base + MX3_PWMSR); 147 fifoav = sr & MX3_PWMSR_FIFOAV_MASK; 148 if (fifoav == MX3_PWMSR_FIFOAV_4WORDS) { 149 period_ms = DIV_ROUND_UP(pwm_get_period(pwm), 150 NSEC_PER_MSEC); 151 msleep(period_ms); 152 153 sr = readl(imx->mmio_base + MX3_PWMSR); 154 if (fifoav == (sr & MX3_PWMSR_FIFOAV_MASK)) 155 dev_warn(dev, "there is no free FIFO slot\n"); 156 } 157} 158 159static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, 160 struct pwm_state *state) 161{ 162 unsigned long period_cycles, duty_cycles, prescale; 163 struct imx_chip *imx = to_imx_chip(chip); 164 struct pwm_state cstate; 165 unsigned long long c; 166 int ret; 167 u32 cr; 168 169 pwm_get_state(pwm, &cstate); 170 171 if (state->enabled) { 172 c = clk_get_rate(imx->clk_per); 173 c *= state->period; 174 175 do_div(c, 1000000000); 176 period_cycles = c; 177 178 prescale = period_cycles / 0x10000 + 1; 179 180 period_cycles /= prescale; 181 c = (unsigned long long)period_cycles * state->duty_cycle; 182 do_div(c, state->period); 183 duty_cycles = c; 184 185 /* 186 * according to imx pwm RM, the real period value should be 187 * PERIOD value in PWMPR plus 2. 188 */ 189 if (period_cycles > 2) 190 period_cycles -= 2; 191 else 192 period_cycles = 0; 193 194 /* 195 * Wait for a free FIFO slot if the PWM is already enabled, and 196 * flush the FIFO if the PWM was disabled and is about to be 197 * enabled. 198 */ 199 if (cstate.enabled) { 200 imx_pwm_wait_fifo_slot(chip, pwm); 201 } else { 202 ret = clk_prepare_enable(imx->clk_per); 203 if (ret) 204 return ret; 205 206 imx_pwm_sw_reset(chip); 207 } 208 209 writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); 210 writel(period_cycles, imx->mmio_base + MX3_PWMPR); 211 212 cr = MX3_PWMCR_PRESCALER(prescale) | 213 MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | 214 MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH | 215 MX3_PWMCR_EN; 216 217 if (state->polarity == PWM_POLARITY_INVERSED) 218 cr |= MX3_PWMCR_POUTC; 219 220 writel(cr, imx->mmio_base + MX3_PWMCR); 221 } else if (cstate.enabled) { 222 writel(0, imx->mmio_base + MX3_PWMCR); 223 224 clk_disable_unprepare(imx->clk_per); 225 } 226 227 return 0; 228} 229 230static const struct pwm_ops imx_pwm_ops_v1 = { 231 .enable = imx_pwm_enable_v1, 232 .disable = imx_pwm_disable_v1, 233 .config = imx_pwm_config_v1, 234 .owner = THIS_MODULE, 235}; 236 237static const struct pwm_ops imx_pwm_ops_v2 = { 238 .apply = imx_pwm_apply_v2, 239 .owner = THIS_MODULE, 240}; 241 242struct imx_pwm_data { 243 bool polarity_supported; 244 const struct pwm_ops *ops; 245}; 246 247static struct imx_pwm_data imx_pwm_data_v1 = { 248 .ops = &imx_pwm_ops_v1, 249}; 250 251static struct imx_pwm_data imx_pwm_data_v2 = { 252 .polarity_supported = true, 253 .ops = &imx_pwm_ops_v2, 254}; 255 256static const struct of_device_id imx_pwm_dt_ids[] = { 257 { .compatible = "fsl,imx1-pwm", .data = &imx_pwm_data_v1, }, 258 { .compatible = "fsl,imx27-pwm", .data = &imx_pwm_data_v2, }, 259 { /* sentinel */ } 260}; 261MODULE_DEVICE_TABLE(of, imx_pwm_dt_ids); 262 263static int imx_pwm_probe(struct platform_device *pdev) 264{ 265 const struct of_device_id *of_id = 266 of_match_device(imx_pwm_dt_ids, &pdev->dev); 267 const struct imx_pwm_data *data; 268 struct imx_chip *imx; 269 struct resource *r; 270 int ret = 0; 271 272 if (!of_id) 273 return -ENODEV; 274 275 data = of_id->data; 276 277 imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); 278 if (imx == NULL) 279 return -ENOMEM; 280 281 imx->clk_per = devm_clk_get(&pdev->dev, "per"); 282 if (IS_ERR(imx->clk_per)) { 283 dev_err(&pdev->dev, "getting per clock failed with %ld\n", 284 PTR_ERR(imx->clk_per)); 285 return PTR_ERR(imx->clk_per); 286 } 287 288 imx->chip.ops = data->ops; 289 imx->chip.dev = &pdev->dev; 290 imx->chip.base = -1; 291 imx->chip.npwm = 1; 292 293 if (data->polarity_supported) { 294 dev_dbg(&pdev->dev, "PWM supports output inversion\n"); 295 imx->chip.of_xlate = of_pwm_xlate_with_flags; 296 imx->chip.of_pwm_n_cells = 3; 297 } 298 299 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 300 imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); 301 if (IS_ERR(imx->mmio_base)) 302 return PTR_ERR(imx->mmio_base); 303 304 ret = pwmchip_add(&imx->chip); 305 if (ret < 0) 306 return ret; 307 308 platform_set_drvdata(pdev, imx); 309 return 0; 310} 311 312static int imx_pwm_remove(struct platform_device *pdev) 313{ 314 struct imx_chip *imx; 315 316 imx = platform_get_drvdata(pdev); 317 if (imx == NULL) 318 return -ENODEV; 319 320 return pwmchip_remove(&imx->chip); 321} 322 323static struct platform_driver imx_pwm_driver = { 324 .driver = { 325 .name = "imx-pwm", 326 .of_match_table = imx_pwm_dt_ids, 327 }, 328 .probe = imx_pwm_probe, 329 .remove = imx_pwm_remove, 330}; 331 332module_platform_driver(imx_pwm_driver); 333 334MODULE_LICENSE("GPL v2"); 335MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");