cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pwm-meson.c (15439B)


      1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
      2/*
      3 * PWM controller driver for Amlogic Meson SoCs.
      4 *
      5 * This PWM is only a set of Gates, Dividers and Counters:
      6 * PWM output is achieved by calculating a clock that permits calculating
      7 * two periods (low and high). The counter then has to be set to switch after
      8 * N cycles for the first half period.
      9 * The hardware has no "polarity" setting. This driver reverses the period
     10 * cycles (the low length is inverted with the high length) for
     11 * PWM_POLARITY_INVERSED. This means that .get_state cannot read the polarity
     12 * from the hardware.
     13 * Setting the duty cycle will disable and re-enable the PWM output.
     14 * Disabling the PWM stops the output immediately (without waiting for the
     15 * current period to complete first).
     16 *
     17 * The public S912 (GXM) datasheet contains some documentation for this PWM
     18 * controller starting on page 543:
     19 * https://dl.khadas.com/Hardware/VIM2/Datasheet/S912_Datasheet_V0.220170314publicversion-Wesion.pdf
     20 * An updated version of this IP block is found in S922X (G12B) SoCs. The
     21 * datasheet contains the description for this IP block revision starting at
     22 * page 1084:
     23 * https://dn.odroid.com/S922X/ODROID-N2/Datasheet/S922X_Public_Datasheet_V0.2.pdf
     24 *
     25 * Copyright (c) 2016 BayLibre, SAS.
     26 * Author: Neil Armstrong <narmstrong@baylibre.com>
     27 * Copyright (C) 2014 Amlogic, Inc.
     28 */
     29
     30#include <linux/bitfield.h>
     31#include <linux/bits.h>
     32#include <linux/clk.h>
     33#include <linux/clk-provider.h>
     34#include <linux/err.h>
     35#include <linux/io.h>
     36#include <linux/kernel.h>
     37#include <linux/math64.h>
     38#include <linux/module.h>
     39#include <linux/of.h>
     40#include <linux/of_device.h>
     41#include <linux/platform_device.h>
     42#include <linux/pwm.h>
     43#include <linux/slab.h>
     44#include <linux/spinlock.h>
     45
     46#define REG_PWM_A		0x0
     47#define REG_PWM_B		0x4
     48#define PWM_LOW_MASK		GENMASK(15, 0)
     49#define PWM_HIGH_MASK		GENMASK(31, 16)
     50
     51#define REG_MISC_AB		0x8
     52#define MISC_B_CLK_EN		BIT(23)
     53#define MISC_A_CLK_EN		BIT(15)
     54#define MISC_CLK_DIV_MASK	0x7f
     55#define MISC_B_CLK_DIV_SHIFT	16
     56#define MISC_A_CLK_DIV_SHIFT	8
     57#define MISC_B_CLK_SEL_SHIFT	6
     58#define MISC_A_CLK_SEL_SHIFT	4
     59#define MISC_CLK_SEL_MASK	0x3
     60#define MISC_B_EN		BIT(1)
     61#define MISC_A_EN		BIT(0)
     62
     63#define MESON_NUM_PWMS		2
     64
     65static struct meson_pwm_channel_data {
     66	u8		reg_offset;
     67	u8		clk_sel_shift;
     68	u8		clk_div_shift;
     69	u32		clk_en_mask;
     70	u32		pwm_en_mask;
     71} meson_pwm_per_channel_data[MESON_NUM_PWMS] = {
     72	{
     73		.reg_offset	= REG_PWM_A,
     74		.clk_sel_shift	= MISC_A_CLK_SEL_SHIFT,
     75		.clk_div_shift	= MISC_A_CLK_DIV_SHIFT,
     76		.clk_en_mask	= MISC_A_CLK_EN,
     77		.pwm_en_mask	= MISC_A_EN,
     78	},
     79	{
     80		.reg_offset	= REG_PWM_B,
     81		.clk_sel_shift	= MISC_B_CLK_SEL_SHIFT,
     82		.clk_div_shift	= MISC_B_CLK_DIV_SHIFT,
     83		.clk_en_mask	= MISC_B_CLK_EN,
     84		.pwm_en_mask	= MISC_B_EN,
     85	}
     86};
     87
     88struct meson_pwm_channel {
     89	unsigned int hi;
     90	unsigned int lo;
     91	u8 pre_div;
     92
     93	struct clk *clk_parent;
     94	struct clk_mux mux;
     95	struct clk *clk;
     96};
     97
     98struct meson_pwm_data {
     99	const char * const *parent_names;
    100	unsigned int num_parents;
    101};
    102
    103struct meson_pwm {
    104	struct pwm_chip chip;
    105	const struct meson_pwm_data *data;
    106	struct meson_pwm_channel channels[MESON_NUM_PWMS];
    107	void __iomem *base;
    108	/*
    109	 * Protects register (write) access to the REG_MISC_AB register
    110	 * that is shared between the two PWMs.
    111	 */
    112	spinlock_t lock;
    113};
    114
    115static inline struct meson_pwm *to_meson_pwm(struct pwm_chip *chip)
    116{
    117	return container_of(chip, struct meson_pwm, chip);
    118}
    119
    120static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
    121{
    122	struct meson_pwm *meson = to_meson_pwm(chip);
    123	struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm];
    124	struct device *dev = chip->dev;
    125	int err;
    126
    127	if (channel->clk_parent) {
    128		err = clk_set_parent(channel->clk, channel->clk_parent);
    129		if (err < 0) {
    130			dev_err(dev, "failed to set parent %s for %s: %d\n",
    131				__clk_get_name(channel->clk_parent),
    132				__clk_get_name(channel->clk), err);
    133			return err;
    134		}
    135	}
    136
    137	err = clk_prepare_enable(channel->clk);
    138	if (err < 0) {
    139		dev_err(dev, "failed to enable clock %s: %d\n",
    140			__clk_get_name(channel->clk), err);
    141		return err;
    142	}
    143
    144	return 0;
    145}
    146
    147static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
    148{
    149	struct meson_pwm *meson = to_meson_pwm(chip);
    150	struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm];
    151
    152	clk_disable_unprepare(channel->clk);
    153}
    154
    155static int meson_pwm_calc(struct meson_pwm *meson, struct pwm_device *pwm,
    156			  const struct pwm_state *state)
    157{
    158	struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm];
    159	unsigned int duty, period, pre_div, cnt, duty_cnt;
    160	unsigned long fin_freq;
    161
    162	duty = state->duty_cycle;
    163	period = state->period;
    164
    165	if (state->polarity == PWM_POLARITY_INVERSED)
    166		duty = period - duty;
    167
    168	fin_freq = clk_get_rate(channel->clk);
    169	if (fin_freq == 0) {
    170		dev_err(meson->chip.dev, "invalid source clock frequency\n");
    171		return -EINVAL;
    172	}
    173
    174	dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq);
    175
    176	pre_div = div64_u64(fin_freq * (u64)period, NSEC_PER_SEC * 0xffffLL);
    177	if (pre_div > MISC_CLK_DIV_MASK) {
    178		dev_err(meson->chip.dev, "unable to get period pre_div\n");
    179		return -EINVAL;
    180	}
    181
    182	cnt = div64_u64(fin_freq * (u64)period, NSEC_PER_SEC * (pre_div + 1));
    183	if (cnt > 0xffff) {
    184		dev_err(meson->chip.dev, "unable to get period cnt\n");
    185		return -EINVAL;
    186	}
    187
    188	dev_dbg(meson->chip.dev, "period=%u pre_div=%u cnt=%u\n", period,
    189		pre_div, cnt);
    190
    191	if (duty == period) {
    192		channel->pre_div = pre_div;
    193		channel->hi = cnt;
    194		channel->lo = 0;
    195	} else if (duty == 0) {
    196		channel->pre_div = pre_div;
    197		channel->hi = 0;
    198		channel->lo = cnt;
    199	} else {
    200		/* Then check is we can have the duty with the same pre_div */
    201		duty_cnt = div64_u64(fin_freq * (u64)duty,
    202				     NSEC_PER_SEC * (pre_div + 1));
    203		if (duty_cnt > 0xffff) {
    204			dev_err(meson->chip.dev, "unable to get duty cycle\n");
    205			return -EINVAL;
    206		}
    207
    208		dev_dbg(meson->chip.dev, "duty=%u pre_div=%u duty_cnt=%u\n",
    209			duty, pre_div, duty_cnt);
    210
    211		channel->pre_div = pre_div;
    212		channel->hi = duty_cnt;
    213		channel->lo = cnt - duty_cnt;
    214	}
    215
    216	return 0;
    217}
    218
    219static void meson_pwm_enable(struct meson_pwm *meson, struct pwm_device *pwm)
    220{
    221	struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm];
    222	struct meson_pwm_channel_data *channel_data;
    223	unsigned long flags;
    224	u32 value;
    225
    226	channel_data = &meson_pwm_per_channel_data[pwm->hwpwm];
    227
    228	spin_lock_irqsave(&meson->lock, flags);
    229
    230	value = readl(meson->base + REG_MISC_AB);
    231	value &= ~(MISC_CLK_DIV_MASK << channel_data->clk_div_shift);
    232	value |= channel->pre_div << channel_data->clk_div_shift;
    233	value |= channel_data->clk_en_mask;
    234	writel(value, meson->base + REG_MISC_AB);
    235
    236	value = FIELD_PREP(PWM_HIGH_MASK, channel->hi) |
    237		FIELD_PREP(PWM_LOW_MASK, channel->lo);
    238	writel(value, meson->base + channel_data->reg_offset);
    239
    240	value = readl(meson->base + REG_MISC_AB);
    241	value |= channel_data->pwm_en_mask;
    242	writel(value, meson->base + REG_MISC_AB);
    243
    244	spin_unlock_irqrestore(&meson->lock, flags);
    245}
    246
    247static void meson_pwm_disable(struct meson_pwm *meson, struct pwm_device *pwm)
    248{
    249	unsigned long flags;
    250	u32 value;
    251
    252	spin_lock_irqsave(&meson->lock, flags);
    253
    254	value = readl(meson->base + REG_MISC_AB);
    255	value &= ~meson_pwm_per_channel_data[pwm->hwpwm].pwm_en_mask;
    256	writel(value, meson->base + REG_MISC_AB);
    257
    258	spin_unlock_irqrestore(&meson->lock, flags);
    259}
    260
    261static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
    262			   const struct pwm_state *state)
    263{
    264	struct meson_pwm *meson = to_meson_pwm(chip);
    265	struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm];
    266	int err = 0;
    267
    268	if (!state->enabled) {
    269		if (state->polarity == PWM_POLARITY_INVERSED) {
    270			/*
    271			 * This IP block revision doesn't have an "always high"
    272			 * setting which we can use for "inverted disabled".
    273			 * Instead we achieve this using the same settings
    274			 * that we use a pre_div of 0 (to get the shortest
    275			 * possible duration for one "count") and
    276			 * "period == duty_cycle". This results in a signal
    277			 * which is LOW for one "count", while being HIGH for
    278			 * the rest of the (so the signal is HIGH for slightly
    279			 * less than 100% of the period, but this is the best
    280			 * we can achieve).
    281			 */
    282			channel->pre_div = 0;
    283			channel->hi = ~0;
    284			channel->lo = 0;
    285
    286			meson_pwm_enable(meson, pwm);
    287		} else {
    288			meson_pwm_disable(meson, pwm);
    289		}
    290	} else {
    291		err = meson_pwm_calc(meson, pwm, state);
    292		if (err < 0)
    293			return err;
    294
    295		meson_pwm_enable(meson, pwm);
    296	}
    297
    298	return 0;
    299}
    300
    301static unsigned int meson_pwm_cnt_to_ns(struct pwm_chip *chip,
    302					struct pwm_device *pwm, u32 cnt)
    303{
    304	struct meson_pwm *meson = to_meson_pwm(chip);
    305	struct meson_pwm_channel *channel;
    306	unsigned long fin_freq;
    307	u32 fin_ns;
    308
    309	/* to_meson_pwm() can only be used after .get_state() is called */
    310	channel = &meson->channels[pwm->hwpwm];
    311
    312	fin_freq = clk_get_rate(channel->clk);
    313	if (fin_freq == 0)
    314		return 0;
    315
    316	fin_ns = div_u64(NSEC_PER_SEC, fin_freq);
    317
    318	return cnt * fin_ns * (channel->pre_div + 1);
    319}
    320
    321static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
    322				struct pwm_state *state)
    323{
    324	struct meson_pwm *meson = to_meson_pwm(chip);
    325	struct meson_pwm_channel_data *channel_data;
    326	struct meson_pwm_channel *channel;
    327	u32 value, tmp;
    328
    329	if (!state)
    330		return;
    331
    332	channel = &meson->channels[pwm->hwpwm];
    333	channel_data = &meson_pwm_per_channel_data[pwm->hwpwm];
    334
    335	value = readl(meson->base + REG_MISC_AB);
    336
    337	tmp = channel_data->pwm_en_mask | channel_data->clk_en_mask;
    338	state->enabled = (value & tmp) == tmp;
    339
    340	tmp = value >> channel_data->clk_div_shift;
    341	channel->pre_div = FIELD_GET(MISC_CLK_DIV_MASK, tmp);
    342
    343	value = readl(meson->base + channel_data->reg_offset);
    344
    345	channel->lo = FIELD_GET(PWM_LOW_MASK, value);
    346	channel->hi = FIELD_GET(PWM_HIGH_MASK, value);
    347
    348	if (channel->lo == 0) {
    349		state->period = meson_pwm_cnt_to_ns(chip, pwm, channel->hi);
    350		state->duty_cycle = state->period;
    351	} else if (channel->lo >= channel->hi) {
    352		state->period = meson_pwm_cnt_to_ns(chip, pwm,
    353						    channel->lo + channel->hi);
    354		state->duty_cycle = meson_pwm_cnt_to_ns(chip, pwm,
    355							channel->hi);
    356	} else {
    357		state->period = 0;
    358		state->duty_cycle = 0;
    359	}
    360}
    361
    362static const struct pwm_ops meson_pwm_ops = {
    363	.request = meson_pwm_request,
    364	.free = meson_pwm_free,
    365	.apply = meson_pwm_apply,
    366	.get_state = meson_pwm_get_state,
    367	.owner = THIS_MODULE,
    368};
    369
    370static const char * const pwm_meson8b_parent_names[] = {
    371	"xtal", "vid_pll", "fclk_div4", "fclk_div3"
    372};
    373
    374static const struct meson_pwm_data pwm_meson8b_data = {
    375	.parent_names = pwm_meson8b_parent_names,
    376	.num_parents = ARRAY_SIZE(pwm_meson8b_parent_names),
    377};
    378
    379static const char * const pwm_gxbb_parent_names[] = {
    380	"xtal", "hdmi_pll", "fclk_div4", "fclk_div3"
    381};
    382
    383static const struct meson_pwm_data pwm_gxbb_data = {
    384	.parent_names = pwm_gxbb_parent_names,
    385	.num_parents = ARRAY_SIZE(pwm_gxbb_parent_names),
    386};
    387
    388/*
    389 * Only the 2 first inputs of the GXBB AO PWMs are valid
    390 * The last 2 are grounded
    391 */
    392static const char * const pwm_gxbb_ao_parent_names[] = {
    393	"xtal", "clk81"
    394};
    395
    396static const struct meson_pwm_data pwm_gxbb_ao_data = {
    397	.parent_names = pwm_gxbb_ao_parent_names,
    398	.num_parents = ARRAY_SIZE(pwm_gxbb_ao_parent_names),
    399};
    400
    401static const char * const pwm_axg_ee_parent_names[] = {
    402	"xtal", "fclk_div5", "fclk_div4", "fclk_div3"
    403};
    404
    405static const struct meson_pwm_data pwm_axg_ee_data = {
    406	.parent_names = pwm_axg_ee_parent_names,
    407	.num_parents = ARRAY_SIZE(pwm_axg_ee_parent_names),
    408};
    409
    410static const char * const pwm_axg_ao_parent_names[] = {
    411	"aoclk81", "xtal", "fclk_div4", "fclk_div5"
    412};
    413
    414static const struct meson_pwm_data pwm_axg_ao_data = {
    415	.parent_names = pwm_axg_ao_parent_names,
    416	.num_parents = ARRAY_SIZE(pwm_axg_ao_parent_names),
    417};
    418
    419static const char * const pwm_g12a_ao_ab_parent_names[] = {
    420	"xtal", "aoclk81", "fclk_div4", "fclk_div5"
    421};
    422
    423static const struct meson_pwm_data pwm_g12a_ao_ab_data = {
    424	.parent_names = pwm_g12a_ao_ab_parent_names,
    425	.num_parents = ARRAY_SIZE(pwm_g12a_ao_ab_parent_names),
    426};
    427
    428static const char * const pwm_g12a_ao_cd_parent_names[] = {
    429	"xtal", "aoclk81",
    430};
    431
    432static const struct meson_pwm_data pwm_g12a_ao_cd_data = {
    433	.parent_names = pwm_g12a_ao_cd_parent_names,
    434	.num_parents = ARRAY_SIZE(pwm_g12a_ao_cd_parent_names),
    435};
    436
    437static const char * const pwm_g12a_ee_parent_names[] = {
    438	"xtal", "hdmi_pll", "fclk_div4", "fclk_div3"
    439};
    440
    441static const struct meson_pwm_data pwm_g12a_ee_data = {
    442	.parent_names = pwm_g12a_ee_parent_names,
    443	.num_parents = ARRAY_SIZE(pwm_g12a_ee_parent_names),
    444};
    445
    446static const struct of_device_id meson_pwm_matches[] = {
    447	{
    448		.compatible = "amlogic,meson8b-pwm",
    449		.data = &pwm_meson8b_data
    450	},
    451	{
    452		.compatible = "amlogic,meson-gxbb-pwm",
    453		.data = &pwm_gxbb_data
    454	},
    455	{
    456		.compatible = "amlogic,meson-gxbb-ao-pwm",
    457		.data = &pwm_gxbb_ao_data
    458	},
    459	{
    460		.compatible = "amlogic,meson-axg-ee-pwm",
    461		.data = &pwm_axg_ee_data
    462	},
    463	{
    464		.compatible = "amlogic,meson-axg-ao-pwm",
    465		.data = &pwm_axg_ao_data
    466	},
    467	{
    468		.compatible = "amlogic,meson-g12a-ee-pwm",
    469		.data = &pwm_g12a_ee_data
    470	},
    471	{
    472		.compatible = "amlogic,meson-g12a-ao-pwm-ab",
    473		.data = &pwm_g12a_ao_ab_data
    474	},
    475	{
    476		.compatible = "amlogic,meson-g12a-ao-pwm-cd",
    477		.data = &pwm_g12a_ao_cd_data
    478	},
    479	{},
    480};
    481MODULE_DEVICE_TABLE(of, meson_pwm_matches);
    482
    483static int meson_pwm_init_channels(struct meson_pwm *meson)
    484{
    485	struct device *dev = meson->chip.dev;
    486	struct clk_init_data init;
    487	unsigned int i;
    488	char name[255];
    489	int err;
    490
    491	for (i = 0; i < meson->chip.npwm; i++) {
    492		struct meson_pwm_channel *channel = &meson->channels[i];
    493
    494		snprintf(name, sizeof(name), "%s#mux%u", dev_name(dev), i);
    495
    496		init.name = name;
    497		init.ops = &clk_mux_ops;
    498		init.flags = 0;
    499		init.parent_names = meson->data->parent_names;
    500		init.num_parents = meson->data->num_parents;
    501
    502		channel->mux.reg = meson->base + REG_MISC_AB;
    503		channel->mux.shift =
    504				meson_pwm_per_channel_data[i].clk_sel_shift;
    505		channel->mux.mask = MISC_CLK_SEL_MASK;
    506		channel->mux.flags = 0;
    507		channel->mux.lock = &meson->lock;
    508		channel->mux.table = NULL;
    509		channel->mux.hw.init = &init;
    510
    511		channel->clk = devm_clk_register(dev, &channel->mux.hw);
    512		if (IS_ERR(channel->clk)) {
    513			err = PTR_ERR(channel->clk);
    514			dev_err(dev, "failed to register %s: %d\n", name, err);
    515			return err;
    516		}
    517
    518		snprintf(name, sizeof(name), "clkin%u", i);
    519
    520		channel->clk_parent = devm_clk_get_optional(dev, name);
    521		if (IS_ERR(channel->clk_parent))
    522			return PTR_ERR(channel->clk_parent);
    523	}
    524
    525	return 0;
    526}
    527
    528static int meson_pwm_probe(struct platform_device *pdev)
    529{
    530	struct meson_pwm *meson;
    531	int err;
    532
    533	meson = devm_kzalloc(&pdev->dev, sizeof(*meson), GFP_KERNEL);
    534	if (!meson)
    535		return -ENOMEM;
    536
    537	meson->base = devm_platform_ioremap_resource(pdev, 0);
    538	if (IS_ERR(meson->base))
    539		return PTR_ERR(meson->base);
    540
    541	spin_lock_init(&meson->lock);
    542	meson->chip.dev = &pdev->dev;
    543	meson->chip.ops = &meson_pwm_ops;
    544	meson->chip.npwm = MESON_NUM_PWMS;
    545
    546	meson->data = of_device_get_match_data(&pdev->dev);
    547
    548	err = meson_pwm_init_channels(meson);
    549	if (err < 0)
    550		return err;
    551
    552	err = devm_pwmchip_add(&pdev->dev, &meson->chip);
    553	if (err < 0) {
    554		dev_err(&pdev->dev, "failed to register PWM chip: %d\n", err);
    555		return err;
    556	}
    557
    558	return 0;
    559}
    560
    561static struct platform_driver meson_pwm_driver = {
    562	.driver = {
    563		.name = "meson-pwm",
    564		.of_match_table = meson_pwm_matches,
    565	},
    566	.probe = meson_pwm_probe,
    567};
    568module_platform_driver(meson_pwm_driver);
    569
    570MODULE_DESCRIPTION("Amlogic Meson PWM Generator driver");
    571MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
    572MODULE_LICENSE("Dual BSD/GPL");