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