12132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva/* 22132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva * Copyright 2012 Alexandre Pereira da Silva <aletes.xgr@gmail.com> 32132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva * 42132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva * This program is free software; you can redistribute it and/or modify 52132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva * it under the terms of the GNU General Public License as published by 62132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva * the Free Software Foundation; version 2. 72132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva * 82132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva */ 92132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 102132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva#include <linux/clk.h> 112132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva#include <linux/err.h> 122132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva#include <linux/io.h> 132132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva#include <linux/kernel.h> 142132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva#include <linux/module.h> 152132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva#include <linux/of.h> 162132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva#include <linux/of_address.h> 172132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva#include <linux/platform_device.h> 182132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva#include <linux/pwm.h> 192132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva#include <linux/slab.h> 202132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 212132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silvastruct lpc32xx_pwm_chip { 222132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva struct pwm_chip chip; 232132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva struct clk *clk; 242132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva void __iomem *base; 252132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva}; 262132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 272132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva#define PWM_ENABLE (1 << 31) 282132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva#define PWM_RELOADV(x) (((x) & 0xFF) << 8) 292132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva#define PWM_DUTY(x) ((x) & 0xFF) 302132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 312132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva#define to_lpc32xx_pwm_chip(_chip) \ 322132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva container_of(_chip, struct lpc32xx_pwm_chip, chip) 332132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 342132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silvastatic int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, 352132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva int duty_ns, int period_ns) 362132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva{ 372132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip); 382132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva unsigned long long c; 392132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva int period_cycles, duty_cycles; 40affb923df96d6c927f9a3dfb0e6f4be940b185cdAxel Lin u32 val; 412132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 422132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva c = clk_get_rate(lpc32xx->clk) / 256; 432132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva c = c * period_ns; 442132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva do_div(c, NSEC_PER_SEC); 452132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 462132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva /* Handle high and low extremes */ 472132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva if (c == 0) 482132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva c = 1; 492132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva if (c > 255) 502132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva c = 0; /* 0 set division by 256 */ 512132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva period_cycles = c; 522132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 53a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel /* The duty-cycle value is as follows: 54a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel * 55a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel * DUTY-CYCLE HIGH LEVEL 56a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel * 1 99.9% 57a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel * 25 90.0% 58a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel * 128 50.0% 59a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel * 220 10.0% 60a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel * 255 0.1% 61a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel * 0 0.0% 62a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel * 63a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel * In other words, the register value is duty-cycle % 256 with 64a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel * duty-cycle in the range 1-256. 65a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel */ 662132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva c = 256 * duty_ns; 672132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva do_div(c, period_ns); 68a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel if (c > 255) 69a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel c = 255; 70a9a18e0691228707230df660dd56364aebf6ea47Alban Bedel duty_cycles = 256 - c; 712132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 72affb923df96d6c927f9a3dfb0e6f4be940b185cdAxel Lin val = readl(lpc32xx->base + (pwm->hwpwm << 2)); 73affb923df96d6c927f9a3dfb0e6f4be940b185cdAxel Lin val &= ~0xFFFF; 74affb923df96d6c927f9a3dfb0e6f4be940b185cdAxel Lin val |= PWM_RELOADV(period_cycles) | PWM_DUTY(duty_cycles); 75affb923df96d6c927f9a3dfb0e6f4be940b185cdAxel Lin writel(val, lpc32xx->base + (pwm->hwpwm << 2)); 762132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 772132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva return 0; 782132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva} 792132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 802132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silvastatic int lpc32xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) 812132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva{ 822132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip); 8308ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin u32 val; 8408ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin int ret; 8508ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin 8608ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin ret = clk_enable(lpc32xx->clk); 8708ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin if (ret) 8808ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin return ret; 8908ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin 9008ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin val = readl(lpc32xx->base + (pwm->hwpwm << 2)); 9108ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin val |= PWM_ENABLE; 9208ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin writel(val, lpc32xx->base + (pwm->hwpwm << 2)); 932132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 9408ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin return 0; 952132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva} 962132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 972132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silvastatic void lpc32xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) 982132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva{ 992132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip); 10008ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin u32 val; 10108ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin 10208ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin val = readl(lpc32xx->base + (pwm->hwpwm << 2)); 10308ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin val &= ~PWM_ENABLE; 10408ee77b5a5de27ad63c92262ebcb4efe0da93b58Axel Lin writel(val, lpc32xx->base + (pwm->hwpwm << 2)); 1052132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 1062132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva clk_disable(lpc32xx->clk); 1072132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva} 1082132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 1092132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silvastatic const struct pwm_ops lpc32xx_pwm_ops = { 1102132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva .config = lpc32xx_pwm_config, 1112132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva .enable = lpc32xx_pwm_enable, 1122132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva .disable = lpc32xx_pwm_disable, 1132132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva .owner = THIS_MODULE, 1142132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva}; 1152132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 1162132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silvastatic int lpc32xx_pwm_probe(struct platform_device *pdev) 1172132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva{ 1182132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva struct lpc32xx_pwm_chip *lpc32xx; 1192132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva struct resource *res; 1202132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva int ret; 1212132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 1222132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva lpc32xx = devm_kzalloc(&pdev->dev, sizeof(*lpc32xx), GFP_KERNEL); 1232132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva if (!lpc32xx) 1242132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva return -ENOMEM; 1252132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 1262132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1276d4294d1634543853febc4287ecf02998fd234e1Thierry Reding lpc32xx->base = devm_ioremap_resource(&pdev->dev, res); 1286d4294d1634543853febc4287ecf02998fd234e1Thierry Reding if (IS_ERR(lpc32xx->base)) 1296d4294d1634543853febc4287ecf02998fd234e1Thierry Reding return PTR_ERR(lpc32xx->base); 1302132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 1312132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva lpc32xx->clk = devm_clk_get(&pdev->dev, NULL); 1322132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva if (IS_ERR(lpc32xx->clk)) 1332132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva return PTR_ERR(lpc32xx->clk); 1342132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 1352132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva lpc32xx->chip.dev = &pdev->dev; 1362132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva lpc32xx->chip.ops = &lpc32xx_pwm_ops; 1372132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva lpc32xx->chip.npwm = 2; 1388fc6d09dcbcdb252e8b029f2a26fe303cc4649ecAlban Bedel lpc32xx->chip.base = -1; 1392132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 1402132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva ret = pwmchip_add(&lpc32xx->chip); 1412132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva if (ret < 0) { 1422132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva dev_err(&pdev->dev, "failed to add PWM chip, error %d\n", ret); 1432132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva return ret; 1442132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva } 1452132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 1462132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva platform_set_drvdata(pdev, lpc32xx); 1472132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 1482132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva return 0; 1492132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva} 1502132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 15177f37917a6f2bd8635b553178bb34bdd80f08e40Bill Pembertonstatic int lpc32xx_pwm_remove(struct platform_device *pdev) 1522132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva{ 1532132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva struct lpc32xx_pwm_chip *lpc32xx = platform_get_drvdata(pdev); 15454b2a999a167510aa6eb54f49ef21a40946c88baAlban Bedel unsigned int i; 15554b2a999a167510aa6eb54f49ef21a40946c88baAlban Bedel 15654b2a999a167510aa6eb54f49ef21a40946c88baAlban Bedel for (i = 0; i < lpc32xx->chip.npwm; i++) 15754b2a999a167510aa6eb54f49ef21a40946c88baAlban Bedel pwm_disable(&lpc32xx->chip.pwms[i]); 1582132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 1592132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva return pwmchip_remove(&lpc32xx->chip); 1602132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva} 1612132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 162f1a8870aeb5baeb58f0613d1f1e1dca967127fbdThierry Redingstatic const struct of_device_id lpc32xx_pwm_dt_ids[] = { 1632132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva { .compatible = "nxp,lpc3220-pwm", }, 1642132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva { /* sentinel */ } 1652132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva}; 1662132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da SilvaMODULE_DEVICE_TABLE(of, lpc32xx_pwm_dt_ids); 1672132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 1682132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silvastatic struct platform_driver lpc32xx_pwm_driver = { 1692132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva .driver = { 1702132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva .name = "lpc32xx-pwm", 1713dd0a909479c1d372341d749b4ff94cd638b57daThierry Reding .owner = THIS_MODULE, 1723cb3b2bfddcdf0bd44b787127b52c2bd1ce9f291Sachin Kamat .of_match_table = lpc32xx_pwm_dt_ids, 1732132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva }, 1742132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva .probe = lpc32xx_pwm_probe, 175fd1091125a1d11fcc635749d0d3dec36904a7a48Bill Pemberton .remove = lpc32xx_pwm_remove, 1762132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva}; 1772132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silvamodule_platform_driver(lpc32xx_pwm_driver); 1782132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da Silva 1792132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da SilvaMODULE_ALIAS("platform:lpc32xx-pwm"); 1802132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da SilvaMODULE_AUTHOR("Alexandre Pereira da Silva <aletes.xgr@gmail.com>"); 1812132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da SilvaMODULE_DESCRIPTION("LPC32XX PWM Driver"); 1822132fa8d95bc13b8b0e307553b04ee3517762ebeAlexandre Pereira da SilvaMODULE_LICENSE("GPL v2"); 183