brintos

brintos / linux-shallow public Read only

0
0
Text · 5.0 KiB · 463710d Raw
217 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * BCM63268 Timer Clock and Reset Controller Driver4 *5 * Copyright (C) 2023 Álvaro Fernández Rojas <noltari@gmail.com>6 */7 8#include <linux/clk-provider.h>9#include <linux/container_of.h>10#include <linux/delay.h>11#include <linux/device.h>12#include <linux/io.h>13#include <linux/platform_device.h>14#include <linux/reset-controller.h>15#include <linux/spinlock.h>16 17#include <dt-bindings/clock/bcm63268-clock.h>18 19#define BCM63268_TIMER_RESET_SLEEP_MIN_US	1000020#define BCM63268_TIMER_RESET_SLEEP_MAX_US	2000021 22struct bcm63268_tclkrst_hw {23	void __iomem *regs;24	spinlock_t lock;25 26	struct reset_controller_dev rcdev;27	struct clk_hw_onecell_data data;28};29 30struct bcm63268_tclk_table_entry {31	const char * const name;32	u8 bit;33};34 35static const struct bcm63268_tclk_table_entry bcm63268_timer_clocks[] = {36	{37		.name = "ephy1",38		.bit = BCM63268_TCLK_EPHY1,39	}, {40		.name = "ephy2",41		.bit = BCM63268_TCLK_EPHY2,42	}, {43		.name = "ephy3",44		.bit = BCM63268_TCLK_EPHY3,45	}, {46		.name = "gphy1",47		.bit = BCM63268_TCLK_GPHY1,48	}, {49		.name = "dsl",50		.bit = BCM63268_TCLK_DSL,51	}, {52		.name = "wakeon_ephy",53		.bit = BCM63268_TCLK_WAKEON_EPHY,54	}, {55		.name = "wakeon_dsl",56		.bit = BCM63268_TCLK_WAKEON_DSL,57	}, {58		.name = "fap1_pll",59		.bit = BCM63268_TCLK_FAP1,60	}, {61		.name = "fap2_pll",62		.bit = BCM63268_TCLK_FAP2,63	}, {64		.name = "uto_50",65		.bit = BCM63268_TCLK_UTO_50,66	}, {67		.name = "uto_extin",68		.bit = BCM63268_TCLK_UTO_EXTIN,69	}, {70		.name = "usb_ref",71		.bit = BCM63268_TCLK_USB_REF,72	}, {73		/* sentinel */74	}75};76 77static inline struct bcm63268_tclkrst_hw *78to_bcm63268_timer_reset(struct reset_controller_dev *rcdev)79{80	return container_of(rcdev, struct bcm63268_tclkrst_hw, rcdev);81}82 83static int bcm63268_timer_reset_update(struct reset_controller_dev *rcdev,84				unsigned long id, bool assert)85{86	struct bcm63268_tclkrst_hw *reset = to_bcm63268_timer_reset(rcdev);87	unsigned long flags;88	uint32_t val;89 90	spin_lock_irqsave(&reset->lock, flags);91	val = __raw_readl(reset->regs);92	if (assert)93		val &= ~BIT(id);94	else95		val |= BIT(id);96	__raw_writel(val, reset->regs);97	spin_unlock_irqrestore(&reset->lock, flags);98 99	return 0;100}101 102static int bcm63268_timer_reset_assert(struct reset_controller_dev *rcdev,103				unsigned long id)104{105	return bcm63268_timer_reset_update(rcdev, id, true);106}107 108static int bcm63268_timer_reset_deassert(struct reset_controller_dev *rcdev,109				  unsigned long id)110{111	return bcm63268_timer_reset_update(rcdev, id, false);112}113 114static int bcm63268_timer_reset_reset(struct reset_controller_dev *rcdev,115			       unsigned long id)116{117	bcm63268_timer_reset_update(rcdev, id, true);118	usleep_range(BCM63268_TIMER_RESET_SLEEP_MIN_US,119		     BCM63268_TIMER_RESET_SLEEP_MAX_US);120 121	bcm63268_timer_reset_update(rcdev, id, false);122	/*123	 * Ensure component is taken out reset state by sleeping also after124	 * deasserting the reset. Otherwise, the component may not be ready125	 * for operation.126	 */127	usleep_range(BCM63268_TIMER_RESET_SLEEP_MIN_US,128		     BCM63268_TIMER_RESET_SLEEP_MAX_US);129 130	return 0;131}132 133static int bcm63268_timer_reset_status(struct reset_controller_dev *rcdev,134				unsigned long id)135{136	struct bcm63268_tclkrst_hw *reset = to_bcm63268_timer_reset(rcdev);137 138	return !(__raw_readl(reset->regs) & BIT(id));139}140 141static const struct reset_control_ops bcm63268_timer_reset_ops = {142	.assert = bcm63268_timer_reset_assert,143	.deassert = bcm63268_timer_reset_deassert,144	.reset = bcm63268_timer_reset_reset,145	.status = bcm63268_timer_reset_status,146};147 148static int bcm63268_tclk_probe(struct platform_device *pdev)149{150	struct device *dev = &pdev->dev;151	const struct bcm63268_tclk_table_entry *entry;152	struct bcm63268_tclkrst_hw *hw;153	struct clk_hw *clk;154	u8 maxbit = 0;155	int i, ret;156 157	for (entry = bcm63268_timer_clocks; entry->name; entry++)158		maxbit = max(maxbit, entry->bit);159	maxbit++;160 161	hw = devm_kzalloc(&pdev->dev, struct_size(hw, data.hws, maxbit),162			  GFP_KERNEL);163	if (!hw)164		return -ENOMEM;165 166	platform_set_drvdata(pdev, hw);167 168	spin_lock_init(&hw->lock);169 170	hw->data.num = maxbit;171	for (i = 0; i < maxbit; i++)172		hw->data.hws[i] = ERR_PTR(-ENODEV);173 174	hw->regs = devm_platform_ioremap_resource(pdev, 0);175	if (IS_ERR(hw->regs))176		return PTR_ERR(hw->regs);177 178	for (entry = bcm63268_timer_clocks; entry->name; entry++) {179		clk = devm_clk_hw_register_gate(dev, entry->name, NULL, 0,180						hw->regs, entry->bit,181						CLK_GATE_BIG_ENDIAN,182						&hw->lock);183		if (IS_ERR(clk))184			return PTR_ERR(clk);185 186		hw->data.hws[entry->bit] = clk;187	}188 189	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,190					  &hw->data);191	if (ret)192		return ret;193 194	hw->rcdev.of_node = dev->of_node;195	hw->rcdev.ops = &bcm63268_timer_reset_ops;196 197	ret = devm_reset_controller_register(dev, &hw->rcdev);198	if (ret)199		dev_err(dev, "Failed to register reset controller\n");200 201	return 0;202}203 204static const struct of_device_id bcm63268_tclk_dt_ids[] = {205	{ .compatible = "brcm,bcm63268-timer-clocks" },206	{ /* sentinel */ }207};208 209static struct platform_driver bcm63268_tclk = {210	.probe = bcm63268_tclk_probe,211	.driver = {212		.name = "bcm63268-timer-clock",213		.of_match_table = bcm63268_tclk_dt_ids,214	},215};216builtin_platform_driver(bcm63268_tclk);217