121 lines · c
1// SPDX-License-Identifier: MIT2/*3 * clock framework for AMD FCH controller block4 *5 * Copyright 2018 Advanced Micro Devices, Inc.6 */7 8#include <linux/clk.h>9#include <linux/clkdev.h>10#include <linux/clk-provider.h>11#include <linux/pci.h>12#include <linux/platform_data/clk-fch.h>13#include <linux/platform_device.h>14 15/* Clock Driving Strength 2 register */16#define CLKDRVSTR2 0x2817/* Clock Control 1 register */18#define MISCCLKCNTL1 0x4019/* Auxiliary clock1 enable bit */20#define OSCCLKENB 221/* 25Mhz auxiliary output clock freq bit */22#define OSCOUT1CLK25MHZ 1623 24#define ST_CLK_48M 025#define ST_CLK_25M 126#define ST_CLK_MUX 227#define ST_CLK_GATE 328#define ST_MAX_CLKS 429 30#define CLK_48M_FIXED 031#define CLK_GATE_FIXED 132#define CLK_MAX_FIXED 233 34/* List of supported CPU ids for clk mux with 25Mhz clk support */35#define AMD_CPU_ID_ST 0x157636 37static const char * const clk_oscout1_parents[] = { "clk48MHz", "clk25MHz" };38static struct clk_hw *hws[ST_MAX_CLKS];39 40static const struct pci_device_id fch_pci_ids[] = {41 { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_ST) },42 { }43};44 45static int fch_clk_probe(struct platform_device *pdev)46{47 struct fch_clk_data *fch_data;48 struct pci_dev *rdev;49 50 fch_data = dev_get_platdata(&pdev->dev);51 if (!fch_data || !fch_data->base)52 return -EINVAL;53 54 rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));55 if (!rdev) {56 dev_err(&pdev->dev, "FCH device not found\n");57 return -ENODEV;58 }59 60 if (pci_match_id(fch_pci_ids, rdev)) {61 hws[ST_CLK_48M] = clk_hw_register_fixed_rate(NULL, "clk48MHz",62 NULL, 0, 48000000);63 hws[ST_CLK_25M] = clk_hw_register_fixed_rate(NULL, "clk25MHz",64 NULL, 0, 25000000);65 66 hws[ST_CLK_MUX] = clk_hw_register_mux(NULL, "oscout1_mux",67 clk_oscout1_parents, ARRAY_SIZE(clk_oscout1_parents),68 0, fch_data->base + CLKDRVSTR2, OSCOUT1CLK25MHZ, 3, 0,69 NULL);70 71 clk_set_parent(hws[ST_CLK_MUX]->clk, hws[ST_CLK_48M]->clk);72 73 hws[ST_CLK_GATE] = clk_hw_register_gate(NULL, "oscout1",74 "oscout1_mux", 0, fch_data->base + MISCCLKCNTL1,75 OSCCLKENB, CLK_GATE_SET_TO_DISABLE, NULL);76 77 devm_clk_hw_register_clkdev(&pdev->dev, hws[ST_CLK_GATE],78 fch_data->name, NULL);79 } else {80 hws[CLK_48M_FIXED] = clk_hw_register_fixed_rate(NULL, "clk48MHz",81 NULL, 0, 48000000);82 83 hws[CLK_GATE_FIXED] = clk_hw_register_gate(NULL, "oscout1",84 "clk48MHz", 0, fch_data->base + MISCCLKCNTL1,85 OSCCLKENB, 0, NULL);86 87 devm_clk_hw_register_clkdev(&pdev->dev, hws[CLK_GATE_FIXED],88 fch_data->name, NULL);89 }90 91 pci_dev_put(rdev);92 return 0;93}94 95static void fch_clk_remove(struct platform_device *pdev)96{97 int i, clks;98 struct pci_dev *rdev;99 100 rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));101 if (!rdev)102 return;103 104 clks = pci_match_id(fch_pci_ids, rdev) ? CLK_MAX_FIXED : ST_MAX_CLKS;105 106 for (i = 0; i < clks; i++)107 clk_hw_unregister(hws[i]);108 109 pci_dev_put(rdev);110}111 112static struct platform_driver fch_clk_driver = {113 .driver = {114 .name = "clk-fch",115 .suppress_bind_attrs = true,116 },117 .probe = fch_clk_probe,118 .remove = fch_clk_remove,119};120builtin_platform_driver(fch_clk_driver);121