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.

pwm: Add support for Meson PWM Controller

Add support for the PWM controller found in the Amlogic SoCs. This
driver supports the Meson8b and GXBB SoCs.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Tested-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Tested-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>

authored by

Neil Armstrong and committed by
Thierry Reding
211ed630 89394cc7

+530
+9
drivers/pwm/Kconfig
··· 262 262 To compile this driver as a module, choose M here: the module 263 263 will be called pwm-lpss-platform. 264 264 265 + config PWM_MESON 266 + tristate "Amlogic Meson PWM driver" 267 + depends on ARCH_MESON 268 + help 269 + The platform driver for Amlogic Meson PWM controller. 270 + 271 + To compile this driver as a module, choose M here: the module 272 + will be called pwm-meson. 273 + 265 274 config PWM_MTK_DISP 266 275 tristate "MediaTek display PWM driver" 267 276 depends on ARCH_MEDIATEK || COMPILE_TEST
+1
drivers/pwm/Makefile
··· 24 24 obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o 25 25 obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o 26 26 obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o 27 + obj-$(CONFIG_PWM_MESON) += pwm-meson.o 27 28 obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o 28 29 obj-$(CONFIG_PWM_MXS) += pwm-mxs.o 29 30 obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o
+520
drivers/pwm/pwm-meson.c
··· 1 + /* 2 + * This file is provided under a dual BSD/GPLv2 license. When using or 3 + * redistributing this file, you may do so under either license. 4 + * 5 + * GPL LICENSE SUMMARY 6 + * 7 + * Copyright (c) 2016 BayLibre, SAS. 8 + * Author: Neil Armstrong <narmstrong@baylibre.com> 9 + * Copyright (C) 2014 Amlogic, Inc. 10 + * 11 + * This program is free software; you can redistribute it and/or modify 12 + * it under the terms of version 2 of the GNU General Public License as 13 + * published by the Free Software Foundation. 14 + * 15 + * This program is distributed in the hope that it will be useful, but 16 + * WITHOUT ANY WARRANTY; without even the implied warranty of 17 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 + * General Public License for more details. 19 + * 20 + * You should have received a copy of the GNU General Public License 21 + * along with this program; if not, see <http://www.gnu.org/licenses/>. 22 + * The full GNU General Public License is included in this distribution 23 + * in the file called COPYING. 24 + * 25 + * BSD LICENSE 26 + * 27 + * Copyright (c) 2016 BayLibre, SAS. 28 + * Author: Neil Armstrong <narmstrong@baylibre.com> 29 + * Copyright (C) 2014 Amlogic, Inc. 30 + * 31 + * Redistribution and use in source and binary forms, with or without 32 + * modification, are permitted provided that the following conditions 33 + * are met: 34 + * 35 + * * Redistributions of source code must retain the above copyright 36 + * notice, this list of conditions and the following disclaimer. 37 + * * Redistributions in binary form must reproduce the above copyright 38 + * notice, this list of conditions and the following disclaimer in 39 + * the documentation and/or other materials provided with the 40 + * distribution. 41 + * * Neither the name of Intel Corporation nor the names of its 42 + * contributors may be used to endorse or promote products derived 43 + * from this software without specific prior written permission. 44 + * 45 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 46 + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 47 + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 48 + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 49 + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 50 + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 51 + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 52 + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 53 + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 54 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 55 + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 56 + */ 57 + 58 + #include <linux/clk.h> 59 + #include <linux/clk-provider.h> 60 + #include <linux/err.h> 61 + #include <linux/io.h> 62 + #include <linux/kernel.h> 63 + #include <linux/module.h> 64 + #include <linux/of.h> 65 + #include <linux/of_device.h> 66 + #include <linux/platform_device.h> 67 + #include <linux/pwm.h> 68 + #include <linux/slab.h> 69 + #include <linux/spinlock.h> 70 + 71 + #define REG_PWM_A 0x0 72 + #define REG_PWM_B 0x4 73 + #define PWM_HIGH_SHIFT 16 74 + 75 + #define REG_MISC_AB 0x8 76 + #define MISC_B_CLK_EN BIT(23) 77 + #define MISC_A_CLK_EN BIT(15) 78 + #define MISC_CLK_DIV_MASK 0x7f 79 + #define MISC_B_CLK_DIV_SHIFT 16 80 + #define MISC_A_CLK_DIV_SHIFT 8 81 + #define MISC_B_CLK_SEL_SHIFT 6 82 + #define MISC_A_CLK_SEL_SHIFT 4 83 + #define MISC_CLK_SEL_WIDTH 2 84 + #define MISC_B_EN BIT(1) 85 + #define MISC_A_EN BIT(0) 86 + 87 + static const unsigned int mux_reg_shifts[] = { 88 + MISC_A_CLK_SEL_SHIFT, 89 + MISC_B_CLK_SEL_SHIFT 90 + }; 91 + 92 + struct meson_pwm_channel { 93 + unsigned int hi; 94 + unsigned int lo; 95 + u8 pre_div; 96 + 97 + struct pwm_state state; 98 + 99 + struct clk *clk_parent; 100 + struct clk_mux mux; 101 + struct clk *clk; 102 + }; 103 + 104 + struct meson_pwm_data { 105 + const char * const *parent_names; 106 + }; 107 + 108 + struct meson_pwm { 109 + struct pwm_chip chip; 110 + const struct meson_pwm_data *data; 111 + void __iomem *base; 112 + u8 inverter_mask; 113 + spinlock_t lock; 114 + }; 115 + 116 + static inline struct meson_pwm *to_meson_pwm(struct pwm_chip *chip) 117 + { 118 + return container_of(chip, struct meson_pwm, chip); 119 + } 120 + 121 + static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 122 + { 123 + struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); 124 + struct device *dev = chip->dev; 125 + int err; 126 + 127 + if (!channel) 128 + return -ENODEV; 129 + 130 + if (channel->clk_parent) { 131 + err = clk_set_parent(channel->clk, channel->clk_parent); 132 + if (err < 0) { 133 + dev_err(dev, "failed to set parent %s for %s: %d\n", 134 + __clk_get_name(channel->clk_parent), 135 + __clk_get_name(channel->clk), err); 136 + return err; 137 + } 138 + } 139 + 140 + err = clk_prepare_enable(channel->clk); 141 + if (err < 0) { 142 + dev_err(dev, "failed to enable clock %s: %d\n", 143 + __clk_get_name(channel->clk), err); 144 + return err; 145 + } 146 + 147 + chip->ops->get_state(chip, pwm, &channel->state); 148 + 149 + return 0; 150 + } 151 + 152 + static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) 153 + { 154 + struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); 155 + 156 + if (channel) 157 + clk_disable_unprepare(channel->clk); 158 + } 159 + 160 + static int meson_pwm_calc(struct meson_pwm *meson, 161 + struct meson_pwm_channel *channel, unsigned int id, 162 + unsigned int duty, unsigned int period) 163 + { 164 + unsigned int pre_div, cnt, duty_cnt; 165 + unsigned long fin_freq = -1, fin_ns; 166 + 167 + if (~(meson->inverter_mask >> id) & 0x1) 168 + duty = period - duty; 169 + 170 + if (period == channel->state.period && 171 + duty == channel->state.duty_cycle) 172 + return 0; 173 + 174 + fin_freq = clk_get_rate(channel->clk); 175 + if (fin_freq == 0) { 176 + dev_err(meson->chip.dev, "invalid source clock frequency\n"); 177 + return -EINVAL; 178 + } 179 + 180 + dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq); 181 + fin_ns = NSEC_PER_SEC / fin_freq; 182 + 183 + /* Calc pre_div with the period */ 184 + for (pre_div = 0; pre_div < MISC_CLK_DIV_MASK; pre_div++) { 185 + cnt = DIV_ROUND_CLOSEST(period, fin_ns * (pre_div + 1)); 186 + dev_dbg(meson->chip.dev, "fin_ns=%lu pre_div=%u cnt=%u\n", 187 + fin_ns, pre_div, cnt); 188 + if (cnt <= 0xffff) 189 + break; 190 + } 191 + 192 + if (pre_div == MISC_CLK_DIV_MASK) { 193 + dev_err(meson->chip.dev, "unable to get period pre_div\n"); 194 + return -EINVAL; 195 + } 196 + 197 + dev_dbg(meson->chip.dev, "period=%u pre_div=%u cnt=%u\n", period, 198 + pre_div, cnt); 199 + 200 + if (duty == period) { 201 + channel->pre_div = pre_div; 202 + channel->hi = cnt; 203 + channel->lo = 0; 204 + } else if (duty == 0) { 205 + channel->pre_div = pre_div; 206 + channel->hi = 0; 207 + channel->lo = cnt; 208 + } else { 209 + /* Then check is we can have the duty with the same pre_div */ 210 + duty_cnt = DIV_ROUND_CLOSEST(duty, fin_ns * (pre_div + 1)); 211 + if (duty_cnt > 0xffff) { 212 + dev_err(meson->chip.dev, "unable to get duty cycle\n"); 213 + return -EINVAL; 214 + } 215 + 216 + dev_dbg(meson->chip.dev, "duty=%u pre_div=%u duty_cnt=%u\n", 217 + duty, pre_div, duty_cnt); 218 + 219 + channel->pre_div = pre_div; 220 + channel->hi = duty_cnt; 221 + channel->lo = cnt - duty_cnt; 222 + } 223 + 224 + return 0; 225 + } 226 + 227 + static void meson_pwm_enable(struct meson_pwm *meson, 228 + struct meson_pwm_channel *channel, 229 + unsigned int id) 230 + { 231 + u32 value, clk_shift, clk_enable, enable; 232 + unsigned int offset; 233 + 234 + switch (id) { 235 + case 0: 236 + clk_shift = MISC_A_CLK_DIV_SHIFT; 237 + clk_enable = MISC_A_CLK_EN; 238 + enable = MISC_A_EN; 239 + offset = REG_PWM_A; 240 + break; 241 + 242 + case 1: 243 + clk_shift = MISC_B_CLK_DIV_SHIFT; 244 + clk_enable = MISC_B_CLK_EN; 245 + enable = MISC_B_EN; 246 + offset = REG_PWM_B; 247 + break; 248 + } 249 + 250 + value = readl(meson->base + REG_MISC_AB); 251 + value &= ~(MISC_CLK_DIV_MASK << clk_shift); 252 + value |= channel->pre_div << clk_shift; 253 + value |= clk_enable; 254 + writel(value, meson->base + REG_MISC_AB); 255 + 256 + value = (channel->hi << PWM_HIGH_SHIFT) | channel->lo; 257 + writel(value, meson->base + offset); 258 + 259 + value = readl(meson->base + REG_MISC_AB); 260 + value |= enable; 261 + writel(value, meson->base + REG_MISC_AB); 262 + } 263 + 264 + static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id) 265 + { 266 + u32 value, enable; 267 + 268 + switch (id) { 269 + case 0: 270 + enable = MISC_A_EN; 271 + break; 272 + 273 + case 1: 274 + enable = MISC_B_EN; 275 + break; 276 + } 277 + 278 + value = readl(meson->base + REG_MISC_AB); 279 + value &= ~enable; 280 + writel(value, meson->base + REG_MISC_AB); 281 + } 282 + 283 + static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, 284 + struct pwm_state *state) 285 + { 286 + struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); 287 + struct meson_pwm *meson = to_meson_pwm(chip); 288 + unsigned long flags; 289 + int err = 0; 290 + 291 + if (!state) 292 + return -EINVAL; 293 + 294 + spin_lock_irqsave(&meson->lock, flags); 295 + 296 + if (!state->enabled) { 297 + meson_pwm_disable(meson, pwm->hwpwm); 298 + channel->state.enabled = false; 299 + 300 + goto unlock; 301 + } 302 + 303 + if (state->period != channel->state.period || 304 + state->duty_cycle != channel->state.duty_cycle || 305 + state->polarity != channel->state.polarity) { 306 + if (channel->state.enabled) { 307 + meson_pwm_disable(meson, pwm->hwpwm); 308 + channel->state.enabled = false; 309 + } 310 + 311 + if (state->polarity != channel->state.polarity) { 312 + if (state->polarity == PWM_POLARITY_NORMAL) 313 + meson->inverter_mask |= BIT(pwm->hwpwm); 314 + else 315 + meson->inverter_mask &= ~BIT(pwm->hwpwm); 316 + } 317 + 318 + err = meson_pwm_calc(meson, channel, pwm->hwpwm, 319 + state->duty_cycle, state->period); 320 + if (err < 0) 321 + goto unlock; 322 + 323 + channel->state.polarity = state->polarity; 324 + channel->state.period = state->period; 325 + channel->state.duty_cycle = state->duty_cycle; 326 + } 327 + 328 + if (state->enabled && !channel->state.enabled) { 329 + meson_pwm_enable(meson, channel, pwm->hwpwm); 330 + channel->state.enabled = true; 331 + } 332 + 333 + unlock: 334 + spin_unlock_irqrestore(&meson->lock, flags); 335 + return err; 336 + } 337 + 338 + static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, 339 + struct pwm_state *state) 340 + { 341 + struct meson_pwm *meson = to_meson_pwm(chip); 342 + u32 value, mask; 343 + 344 + if (!state) 345 + return; 346 + 347 + switch (pwm->hwpwm) { 348 + case 0: 349 + mask = MISC_A_EN; 350 + break; 351 + 352 + case 1: 353 + mask = MISC_B_EN; 354 + break; 355 + } 356 + 357 + value = readl(meson->base + REG_MISC_AB); 358 + state->enabled = (value & mask) != 0; 359 + } 360 + 361 + static const struct pwm_ops meson_pwm_ops = { 362 + .request = meson_pwm_request, 363 + .free = meson_pwm_free, 364 + .apply = meson_pwm_apply, 365 + .get_state = meson_pwm_get_state, 366 + .owner = THIS_MODULE, 367 + }; 368 + 369 + static const char * const pwm_meson8b_parent_names[] = { 370 + "xtal", "vid_pll", "fclk_div4", "fclk_div3" 371 + }; 372 + 373 + static const struct meson_pwm_data pwm_meson8b_data = { 374 + .parent_names = pwm_meson8b_parent_names, 375 + }; 376 + 377 + static const char * const pwm_gxbb_parent_names[] = { 378 + "xtal", "hdmi_pll", "fclk_div4", "fclk_div3" 379 + }; 380 + 381 + static const struct meson_pwm_data pwm_gxbb_data = { 382 + .parent_names = pwm_gxbb_parent_names, 383 + }; 384 + 385 + static const struct of_device_id meson_pwm_matches[] = { 386 + { .compatible = "amlogic,meson8b-pwm", .data = &pwm_meson8b_data }, 387 + { .compatible = "amlogic,meson-gxbb-pwm", .data = &pwm_gxbb_data }, 388 + {}, 389 + }; 390 + MODULE_DEVICE_TABLE(of, meson_pwm_matches); 391 + 392 + static int meson_pwm_init_channels(struct meson_pwm *meson, 393 + struct meson_pwm_channel *channels) 394 + { 395 + struct device *dev = meson->chip.dev; 396 + struct device_node *np = dev->of_node; 397 + struct clk_init_data init; 398 + unsigned int i; 399 + char name[255]; 400 + int err; 401 + 402 + for (i = 0; i < meson->chip.npwm; i++) { 403 + struct meson_pwm_channel *channel = &channels[i]; 404 + 405 + snprintf(name, sizeof(name), "%s#mux%u", np->full_name, i); 406 + 407 + init.name = name; 408 + init.ops = &clk_mux_ops; 409 + init.flags = CLK_IS_BASIC; 410 + init.parent_names = meson->data->parent_names; 411 + init.num_parents = 1 << MISC_CLK_SEL_WIDTH; 412 + 413 + channel->mux.reg = meson->base + REG_MISC_AB; 414 + channel->mux.shift = mux_reg_shifts[i]; 415 + channel->mux.mask = BIT(MISC_CLK_SEL_WIDTH) - 1; 416 + channel->mux.flags = 0; 417 + channel->mux.lock = &meson->lock; 418 + channel->mux.table = NULL; 419 + channel->mux.hw.init = &init; 420 + 421 + channel->clk = devm_clk_register(dev, &channel->mux.hw); 422 + if (IS_ERR(channel->clk)) { 423 + err = PTR_ERR(channel->clk); 424 + dev_err(dev, "failed to register %s: %d\n", name, err); 425 + return err; 426 + } 427 + 428 + snprintf(name, sizeof(name), "clkin%u", i); 429 + 430 + channel->clk_parent = devm_clk_get(dev, name); 431 + if (IS_ERR(channel->clk_parent)) { 432 + err = PTR_ERR(channel->clk_parent); 433 + if (err == -EPROBE_DEFER) 434 + return err; 435 + 436 + channel->clk_parent = NULL; 437 + } 438 + } 439 + 440 + return 0; 441 + } 442 + 443 + static void meson_pwm_add_channels(struct meson_pwm *meson, 444 + struct meson_pwm_channel *channels) 445 + { 446 + unsigned int i; 447 + 448 + for (i = 0; i < meson->chip.npwm; i++) 449 + pwm_set_chip_data(&meson->chip.pwms[i], &channels[i]); 450 + } 451 + 452 + static int meson_pwm_probe(struct platform_device *pdev) 453 + { 454 + struct meson_pwm_channel *channels; 455 + struct meson_pwm *meson; 456 + struct resource *regs; 457 + int err; 458 + 459 + meson = devm_kzalloc(&pdev->dev, sizeof(*meson), GFP_KERNEL); 460 + if (!meson) 461 + return -ENOMEM; 462 + 463 + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 464 + meson->base = devm_ioremap_resource(&pdev->dev, regs); 465 + if (IS_ERR(meson->base)) 466 + return PTR_ERR(meson->base); 467 + 468 + meson->chip.dev = &pdev->dev; 469 + meson->chip.ops = &meson_pwm_ops; 470 + meson->chip.base = -1; 471 + meson->chip.npwm = 2; 472 + meson->chip.of_xlate = of_pwm_xlate_with_flags; 473 + meson->chip.of_pwm_n_cells = 3; 474 + 475 + meson->data = of_device_get_match_data(&pdev->dev); 476 + meson->inverter_mask = BIT(meson->chip.npwm) - 1; 477 + 478 + channels = devm_kcalloc(&pdev->dev, meson->chip.npwm, sizeof(*meson), 479 + GFP_KERNEL); 480 + if (!channels) 481 + return -ENOMEM; 482 + 483 + err = meson_pwm_init_channels(meson, channels); 484 + if (err < 0) 485 + return err; 486 + 487 + err = pwmchip_add(&meson->chip); 488 + if (err < 0) { 489 + dev_err(&pdev->dev, "failed to register PWM chip: %d\n", err); 490 + return err; 491 + } 492 + 493 + meson_pwm_add_channels(meson, channels); 494 + 495 + platform_set_drvdata(pdev, meson); 496 + 497 + return 0; 498 + } 499 + 500 + static int meson_pwm_remove(struct platform_device *pdev) 501 + { 502 + struct meson_pwm *meson = platform_get_drvdata(pdev); 503 + 504 + return pwmchip_remove(&meson->chip); 505 + } 506 + 507 + static struct platform_driver meson_pwm_driver = { 508 + .driver = { 509 + .name = "meson-pwm", 510 + .of_match_table = meson_pwm_matches, 511 + }, 512 + .probe = meson_pwm_probe, 513 + .remove = meson_pwm_remove, 514 + }; 515 + module_platform_driver(meson_pwm_driver); 516 + 517 + MODULE_ALIAS("platform:meson-pwm"); 518 + MODULE_DESCRIPTION("Amlogic Meson PWM Generator driver"); 519 + MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 520 + MODULE_LICENSE("Dual BSD/GPL");