162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// clk-max77686.c - Clock driver for Maxim 77686/MAX77802 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (C) 2012 Samsung Electornics 662306a36Sopenharmony_ci// Jonghwa Lee <jonghwa3.lee@samsung.com> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/mfd/max77620.h> 1462306a36Sopenharmony_ci#include <linux/mfd/max77686.h> 1562306a36Sopenharmony_ci#include <linux/mfd/max77686-private.h> 1662306a36Sopenharmony_ci#include <linux/clk-provider.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/clkdev.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/regmap.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <dt-bindings/clock/maxim,max77686.h> 2362306a36Sopenharmony_ci#include <dt-bindings/clock/maxim,max77802.h> 2462306a36Sopenharmony_ci#include <dt-bindings/clock/maxim,max77620.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define MAX77802_CLOCK_LOW_JITTER_SHIFT 0x3 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cienum max77686_chip_name { 2962306a36Sopenharmony_ci CHIP_MAX77686, 3062306a36Sopenharmony_ci CHIP_MAX77802, 3162306a36Sopenharmony_ci CHIP_MAX77620, 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct max77686_hw_clk_info { 3562306a36Sopenharmony_ci const char *name; 3662306a36Sopenharmony_ci u32 clk_reg; 3762306a36Sopenharmony_ci u32 clk_enable_mask; 3862306a36Sopenharmony_ci u32 flags; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct max77686_clk_init_data { 4262306a36Sopenharmony_ci struct regmap *regmap; 4362306a36Sopenharmony_ci struct clk_hw hw; 4462306a36Sopenharmony_ci struct clk_init_data clk_idata; 4562306a36Sopenharmony_ci const struct max77686_hw_clk_info *clk_info; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct max77686_clk_driver_data { 4962306a36Sopenharmony_ci enum max77686_chip_name chip; 5062306a36Sopenharmony_ci struct max77686_clk_init_data *max_clk_data; 5162306a36Sopenharmony_ci size_t num_clks; 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic const struct 5562306a36Sopenharmony_cimax77686_hw_clk_info max77686_hw_clks_info[MAX77686_CLKS_NUM] = { 5662306a36Sopenharmony_ci [MAX77686_CLK_AP] = { 5762306a36Sopenharmony_ci .name = "32khz_ap", 5862306a36Sopenharmony_ci .clk_reg = MAX77686_REG_32KHZ, 5962306a36Sopenharmony_ci .clk_enable_mask = BIT(MAX77686_CLK_AP), 6062306a36Sopenharmony_ci }, 6162306a36Sopenharmony_ci [MAX77686_CLK_CP] = { 6262306a36Sopenharmony_ci .name = "32khz_cp", 6362306a36Sopenharmony_ci .clk_reg = MAX77686_REG_32KHZ, 6462306a36Sopenharmony_ci .clk_enable_mask = BIT(MAX77686_CLK_CP), 6562306a36Sopenharmony_ci }, 6662306a36Sopenharmony_ci [MAX77686_CLK_PMIC] = { 6762306a36Sopenharmony_ci .name = "32khz_pmic", 6862306a36Sopenharmony_ci .clk_reg = MAX77686_REG_32KHZ, 6962306a36Sopenharmony_ci .clk_enable_mask = BIT(MAX77686_CLK_PMIC), 7062306a36Sopenharmony_ci }, 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic const struct 7462306a36Sopenharmony_cimax77686_hw_clk_info max77802_hw_clks_info[MAX77802_CLKS_NUM] = { 7562306a36Sopenharmony_ci [MAX77802_CLK_32K_AP] = { 7662306a36Sopenharmony_ci .name = "32khz_ap", 7762306a36Sopenharmony_ci .clk_reg = MAX77802_REG_32KHZ, 7862306a36Sopenharmony_ci .clk_enable_mask = BIT(MAX77802_CLK_32K_AP), 7962306a36Sopenharmony_ci }, 8062306a36Sopenharmony_ci [MAX77802_CLK_32K_CP] = { 8162306a36Sopenharmony_ci .name = "32khz_cp", 8262306a36Sopenharmony_ci .clk_reg = MAX77802_REG_32KHZ, 8362306a36Sopenharmony_ci .clk_enable_mask = BIT(MAX77802_CLK_32K_CP), 8462306a36Sopenharmony_ci }, 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic const struct 8862306a36Sopenharmony_cimax77686_hw_clk_info max77620_hw_clks_info[MAX77620_CLKS_NUM] = { 8962306a36Sopenharmony_ci [MAX77620_CLK_32K_OUT0] = { 9062306a36Sopenharmony_ci .name = "32khz_out0", 9162306a36Sopenharmony_ci .clk_reg = MAX77620_REG_CNFG1_32K, 9262306a36Sopenharmony_ci .clk_enable_mask = MAX77620_CNFG1_32K_OUT0_EN, 9362306a36Sopenharmony_ci }, 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic struct max77686_clk_init_data *to_max77686_clk_init_data( 9762306a36Sopenharmony_ci struct clk_hw *hw) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci return container_of(hw, struct max77686_clk_init_data, hw); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int max77686_clk_prepare(struct clk_hw *hw) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return regmap_update_bits(max77686->regmap, max77686->clk_info->clk_reg, 10762306a36Sopenharmony_ci max77686->clk_info->clk_enable_mask, 10862306a36Sopenharmony_ci max77686->clk_info->clk_enable_mask); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void max77686_clk_unprepare(struct clk_hw *hw) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci regmap_update_bits(max77686->regmap, max77686->clk_info->clk_reg, 11662306a36Sopenharmony_ci max77686->clk_info->clk_enable_mask, 11762306a36Sopenharmony_ci ~max77686->clk_info->clk_enable_mask); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int max77686_clk_is_prepared(struct clk_hw *hw) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct max77686_clk_init_data *max77686 = to_max77686_clk_init_data(hw); 12362306a36Sopenharmony_ci int ret; 12462306a36Sopenharmony_ci u32 val; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci ret = regmap_read(max77686->regmap, max77686->clk_info->clk_reg, &val); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (ret < 0) 12962306a36Sopenharmony_ci return -EINVAL; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return val & max77686->clk_info->clk_enable_mask; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic unsigned long max77686_recalc_rate(struct clk_hw *hw, 13562306a36Sopenharmony_ci unsigned long parent_rate) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci return 32768; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic const struct clk_ops max77686_clk_ops = { 14162306a36Sopenharmony_ci .prepare = max77686_clk_prepare, 14262306a36Sopenharmony_ci .unprepare = max77686_clk_unprepare, 14362306a36Sopenharmony_ci .is_prepared = max77686_clk_is_prepared, 14462306a36Sopenharmony_ci .recalc_rate = max77686_recalc_rate, 14562306a36Sopenharmony_ci}; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic struct clk_hw * 14862306a36Sopenharmony_ciof_clk_max77686_get(struct of_phandle_args *clkspec, void *data) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct max77686_clk_driver_data *drv_data = data; 15162306a36Sopenharmony_ci unsigned int idx = clkspec->args[0]; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (idx >= drv_data->num_clks) { 15462306a36Sopenharmony_ci pr_err("%s: invalid index %u\n", __func__, idx); 15562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return &drv_data->max_clk_data[idx].hw; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic int max77686_clk_probe(struct platform_device *pdev) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 16462306a36Sopenharmony_ci struct device *parent = dev->parent; 16562306a36Sopenharmony_ci const struct platform_device_id *id = platform_get_device_id(pdev); 16662306a36Sopenharmony_ci struct max77686_clk_driver_data *drv_data; 16762306a36Sopenharmony_ci const struct max77686_hw_clk_info *hw_clks; 16862306a36Sopenharmony_ci struct regmap *regmap; 16962306a36Sopenharmony_ci int i, ret, num_clks; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); 17262306a36Sopenharmony_ci if (!drv_data) 17362306a36Sopenharmony_ci return -ENOMEM; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci regmap = dev_get_regmap(parent, NULL); 17662306a36Sopenharmony_ci if (!regmap) { 17762306a36Sopenharmony_ci dev_err(dev, "Failed to get rtc regmap\n"); 17862306a36Sopenharmony_ci return -ENODEV; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci drv_data->chip = id->driver_data; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci switch (drv_data->chip) { 18462306a36Sopenharmony_ci case CHIP_MAX77686: 18562306a36Sopenharmony_ci num_clks = MAX77686_CLKS_NUM; 18662306a36Sopenharmony_ci hw_clks = max77686_hw_clks_info; 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci case CHIP_MAX77802: 19062306a36Sopenharmony_ci num_clks = MAX77802_CLKS_NUM; 19162306a36Sopenharmony_ci hw_clks = max77802_hw_clks_info; 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci case CHIP_MAX77620: 19562306a36Sopenharmony_ci num_clks = MAX77620_CLKS_NUM; 19662306a36Sopenharmony_ci hw_clks = max77620_hw_clks_info; 19762306a36Sopenharmony_ci break; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci default: 20062306a36Sopenharmony_ci dev_err(dev, "Unknown Chip ID\n"); 20162306a36Sopenharmony_ci return -EINVAL; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci drv_data->num_clks = num_clks; 20562306a36Sopenharmony_ci drv_data->max_clk_data = devm_kcalloc(dev, num_clks, 20662306a36Sopenharmony_ci sizeof(*drv_data->max_clk_data), 20762306a36Sopenharmony_ci GFP_KERNEL); 20862306a36Sopenharmony_ci if (!drv_data->max_clk_data) 20962306a36Sopenharmony_ci return -ENOMEM; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci for (i = 0; i < num_clks; i++) { 21262306a36Sopenharmony_ci struct max77686_clk_init_data *max_clk_data; 21362306a36Sopenharmony_ci const char *clk_name; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci max_clk_data = &drv_data->max_clk_data[i]; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci max_clk_data->regmap = regmap; 21862306a36Sopenharmony_ci max_clk_data->clk_info = &hw_clks[i]; 21962306a36Sopenharmony_ci max_clk_data->clk_idata.flags = hw_clks[i].flags; 22062306a36Sopenharmony_ci max_clk_data->clk_idata.ops = &max77686_clk_ops; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (parent->of_node && 22362306a36Sopenharmony_ci !of_property_read_string_index(parent->of_node, 22462306a36Sopenharmony_ci "clock-output-names", 22562306a36Sopenharmony_ci i, &clk_name)) 22662306a36Sopenharmony_ci max_clk_data->clk_idata.name = clk_name; 22762306a36Sopenharmony_ci else 22862306a36Sopenharmony_ci max_clk_data->clk_idata.name = hw_clks[i].name; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci max_clk_data->hw.init = &max_clk_data->clk_idata; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci ret = devm_clk_hw_register(dev, &max_clk_data->hw); 23362306a36Sopenharmony_ci if (ret) { 23462306a36Sopenharmony_ci dev_err(dev, "Failed to clock register: %d\n", ret); 23562306a36Sopenharmony_ci return ret; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ret = devm_clk_hw_register_clkdev(dev, &max_clk_data->hw, 23962306a36Sopenharmony_ci max_clk_data->clk_idata.name, 24062306a36Sopenharmony_ci NULL); 24162306a36Sopenharmony_ci if (ret < 0) { 24262306a36Sopenharmony_ci dev_err(dev, "Failed to clkdev register: %d\n", ret); 24362306a36Sopenharmony_ci return ret; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (parent->of_node) { 24862306a36Sopenharmony_ci ret = devm_of_clk_add_hw_provider(dev, of_clk_max77686_get, 24962306a36Sopenharmony_ci drv_data); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (ret < 0) { 25262306a36Sopenharmony_ci dev_err(dev, "Failed to register OF clock provider: %d\n", 25362306a36Sopenharmony_ci ret); 25462306a36Sopenharmony_ci return ret; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* MAX77802: Enable low-jitter mode on the 32khz clocks. */ 25962306a36Sopenharmony_ci if (drv_data->chip == CHIP_MAX77802) { 26062306a36Sopenharmony_ci ret = regmap_update_bits(regmap, MAX77802_REG_32KHZ, 26162306a36Sopenharmony_ci 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT, 26262306a36Sopenharmony_ci 1 << MAX77802_CLOCK_LOW_JITTER_SHIFT); 26362306a36Sopenharmony_ci if (ret < 0) { 26462306a36Sopenharmony_ci dev_err(dev, "Failed to config low-jitter: %d\n", ret); 26562306a36Sopenharmony_ci return ret; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic const struct platform_device_id max77686_clk_id[] = { 27362306a36Sopenharmony_ci { "max77686-clk", .driver_data = CHIP_MAX77686, }, 27462306a36Sopenharmony_ci { "max77802-clk", .driver_data = CHIP_MAX77802, }, 27562306a36Sopenharmony_ci { "max77620-clock", .driver_data = CHIP_MAX77620, }, 27662306a36Sopenharmony_ci {}, 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, max77686_clk_id); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic struct platform_driver max77686_clk_driver = { 28162306a36Sopenharmony_ci .driver = { 28262306a36Sopenharmony_ci .name = "max77686-clk", 28362306a36Sopenharmony_ci }, 28462306a36Sopenharmony_ci .probe = max77686_clk_probe, 28562306a36Sopenharmony_ci .id_table = max77686_clk_id, 28662306a36Sopenharmony_ci}; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cimodule_platform_driver(max77686_clk_driver); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ciMODULE_DESCRIPTION("MAXIM 77686 Clock Driver"); 29162306a36Sopenharmony_ciMODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>"); 29262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 293