1 /* 2 * Intel Low Power Subsystem PWM controller driver 3 * 4 * Copyright (C) 2014, Intel Corporation 5 * Author: Mika Westerberg <mika.westerberg@linux.intel.com> 6 * Author: Chew Kean Ho <kean.ho.chew@intel.com> 7 * Author: Chang Rebecca Swee Fun <rebecca.swee.fun.chang@intel.com> 8 * Author: Chew Chiau Ee <chiau.ee.chew@intel.com> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 */ 14 15 #include <linux/acpi.h> 16 #include <linux/clk.h> 17 #include <linux/device.h> 18 #include <linux/kernel.h> 19 #include <linux/module.h> 20 #include <linux/pwm.h> 21 #include <linux/platform_device.h> 22 23 #define PWM 0x00000000 24 #define PWM_ENABLE BIT(31) 25 #define PWM_SW_UPDATE BIT(30) 26 #define PWM_BASE_UNIT_SHIFT 8 27 #define PWM_BASE_UNIT_MASK 0x00ffff00 28 #define PWM_ON_TIME_DIV_MASK 0x000000ff 29 #define PWM_DIVISION_CORRECTION 0x2 30 #define PWM_LIMIT (0x8000 + PWM_DIVISION_CORRECTION) 31 #define NSECS_PER_SEC 1000000000UL 32 33 struct pwm_lpss_chip { 34 struct pwm_chip chip; 35 void __iomem *regs; 36 struct clk *clk; 37 }; 38 39 static inline struct pwm_lpss_chip *to_lpwm(struct pwm_chip *chip) 40 { 41 return container_of(chip, struct pwm_lpss_chip, chip); 42 } 43 44 static int pwm_lpss_config(struct pwm_chip *chip, struct pwm_device *pwm, 45 int duty_ns, int period_ns) 46 { 47 struct pwm_lpss_chip *lpwm = to_lpwm(chip); 48 u8 on_time_div; 49 unsigned long c; 50 unsigned long long base_unit, freq = NSECS_PER_SEC; 51 u32 ctrl; 52 53 do_div(freq, period_ns); 54 55 /* The equation is: base_unit = ((freq / c) * 65536) + correction */ 56 base_unit = freq * 65536; 57 58 c = clk_get_rate(lpwm->clk); 59 if (!c) 60 return -EINVAL; 61 62 do_div(base_unit, c); 63 base_unit += PWM_DIVISION_CORRECTION; 64 if (base_unit > PWM_LIMIT) 65 return -EINVAL; 66 67 if (duty_ns <= 0) 68 duty_ns = 1; 69 on_time_div = 255 - (255 * duty_ns / period_ns); 70 71 ctrl = readl(lpwm->regs + PWM); 72 ctrl &= ~(PWM_BASE_UNIT_MASK | PWM_ON_TIME_DIV_MASK); 73 ctrl |= (u16) base_unit << PWM_BASE_UNIT_SHIFT; 74 ctrl |= on_time_div; 75 /* request PWM to update on next cycle */ 76 ctrl |= PWM_SW_UPDATE; 77 writel(ctrl, lpwm->regs + PWM); 78 79 return 0; 80 } 81 82 static int pwm_lpss_enable(struct pwm_chip *chip, struct pwm_device *pwm) 83 { 84 struct pwm_lpss_chip *lpwm = to_lpwm(chip); 85 u32 ctrl; 86 int ret; 87 88 ret = clk_prepare_enable(lpwm->clk); 89 if (ret) 90 return ret; 91 92 ctrl = readl(lpwm->regs + PWM); 93 writel(ctrl | PWM_ENABLE, lpwm->regs + PWM); 94 95 return 0; 96 } 97 98 static void pwm_lpss_disable(struct pwm_chip *chip, struct pwm_device *pwm) 99 { 100 struct pwm_lpss_chip *lpwm = to_lpwm(chip); 101 u32 ctrl; 102 103 ctrl = readl(lpwm->regs + PWM); 104 writel(ctrl & ~PWM_ENABLE, lpwm->regs + PWM); 105 106 clk_disable_unprepare(lpwm->clk); 107 } 108 109 static const struct pwm_ops pwm_lpss_ops = { 110 .config = pwm_lpss_config, 111 .enable = pwm_lpss_enable, 112 .disable = pwm_lpss_disable, 113 .owner = THIS_MODULE, 114 }; 115 116 static const struct acpi_device_id pwm_lpss_acpi_match[] = { 117 { "80860F09", 0 }, 118 { }, 119 }; 120 MODULE_DEVICE_TABLE(acpi, pwm_lpss_acpi_match); 121 122 static int pwm_lpss_probe(struct platform_device *pdev) 123 { 124 struct pwm_lpss_chip *lpwm; 125 struct resource *r; 126 int ret; 127 128 lpwm = devm_kzalloc(&pdev->dev, sizeof(*lpwm), GFP_KERNEL); 129 if (!lpwm) 130 return -ENOMEM; 131 132 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 133 134 lpwm->regs = devm_ioremap_resource(&pdev->dev, r); 135 if (IS_ERR(lpwm->regs)) 136 return PTR_ERR(lpwm->regs); 137 138 lpwm->clk = devm_clk_get(&pdev->dev, NULL); 139 if (IS_ERR(lpwm->clk)) { 140 dev_err(&pdev->dev, "failed to get PWM clock\n"); 141 return PTR_ERR(lpwm->clk); 142 } 143 144 lpwm->chip.dev = &pdev->dev; 145 lpwm->chip.ops = &pwm_lpss_ops; 146 lpwm->chip.base = -1; 147 lpwm->chip.npwm = 1; 148 149 ret = pwmchip_add(&lpwm->chip); 150 if (ret) { 151 dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); 152 return ret; 153 } 154 155 platform_set_drvdata(pdev, lpwm); 156 return 0; 157 } 158 159 static int pwm_lpss_remove(struct platform_device *pdev) 160 { 161 struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev); 162 u32 ctrl; 163 164 ctrl = readl(lpwm->regs + PWM); 165 writel(ctrl & ~PWM_ENABLE, lpwm->regs + PWM); 166 167 return pwmchip_remove(&lpwm->chip); 168 } 169 170 static struct platform_driver pwm_lpss_driver = { 171 .driver = { 172 .name = "pwm-lpss", 173 .acpi_match_table = pwm_lpss_acpi_match, 174 }, 175 .probe = pwm_lpss_probe, 176 .remove = pwm_lpss_remove, 177 }; 178 module_platform_driver(pwm_lpss_driver); 179 180 MODULE_DESCRIPTION("PWM driver for Intel LPSS"); 181 MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>"); 182 MODULE_LICENSE("GPL v2"); 183 MODULE_ALIAS("platform:pwm-lpss"); 184