1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Clock driver for twl device. 4 * 5 * inspired by the driver for the Palmas device 6 */ 7 8 #include <linux/clk-provider.h> 9 #include <linux/mfd/twl.h> 10 #include <linux/module.h> 11 #include <linux/platform_device.h> 12 #include <linux/slab.h> 13 14 #define VREG_STATE 2 15 #define TWL6030_CFG_STATE_OFF 0x00 16 #define TWL6030_CFG_STATE_ON 0x01 17 #define TWL6030_CFG_STATE_MASK 0x03 18 19 struct twl_clock_info { 20 struct device *dev; 21 u8 base; 22 struct clk_hw hw; 23 }; 24 25 static inline int 26 twlclk_read(struct twl_clock_info *info, unsigned int slave_subgp, 27 unsigned int offset) 28 { 29 u8 value; 30 int status; 31 32 status = twl_i2c_read_u8(slave_subgp, &value, 33 info->base + offset); 34 return (status < 0) ? status : value; 35 } 36 37 static inline int 38 twlclk_write(struct twl_clock_info *info, unsigned int slave_subgp, 39 unsigned int offset, u8 value) 40 { 41 return twl_i2c_write_u8(slave_subgp, value, 42 info->base + offset); 43 } 44 45 static inline struct twl_clock_info *to_twl_clks_info(struct clk_hw *hw) 46 { 47 return container_of(hw, struct twl_clock_info, hw); 48 } 49 50 static unsigned long twl_clks_recalc_rate(struct clk_hw *hw, 51 unsigned long parent_rate) 52 { 53 return 32768; 54 } 55 56 static int twl6032_clks_prepare(struct clk_hw *hw) 57 { 58 struct twl_clock_info *cinfo = to_twl_clks_info(hw); 59 int ret; 60 61 ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE, 62 TWL6030_CFG_STATE_ON); 63 if (ret < 0) 64 dev_err(cinfo->dev, "clk prepare failed\n"); 65 66 return ret; 67 } 68 69 static void twl6032_clks_unprepare(struct clk_hw *hw) 70 { 71 struct twl_clock_info *cinfo = to_twl_clks_info(hw); 72 int ret; 73 74 ret = twlclk_write(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE, 75 TWL6030_CFG_STATE_OFF); 76 if (ret < 0) 77 dev_err(cinfo->dev, "clk unprepare failed\n"); 78 } 79 80 static int twl6032_clks_is_prepared(struct clk_hw *hw) 81 { 82 struct twl_clock_info *cinfo = to_twl_clks_info(hw); 83 int val; 84 85 val = twlclk_read(cinfo, TWL_MODULE_PM_RECEIVER, VREG_STATE); 86 if (val < 0) { 87 dev_err(cinfo->dev, "clk read failed\n"); 88 return val; 89 } 90 91 val &= TWL6030_CFG_STATE_MASK; 92 93 return val == TWL6030_CFG_STATE_ON; 94 } 95 96 static const struct clk_ops twl6032_clks_ops = { 97 .prepare = twl6032_clks_prepare, 98 .unprepare = twl6032_clks_unprepare, 99 .is_prepared = twl6032_clks_is_prepared, 100 .recalc_rate = twl_clks_recalc_rate, 101 }; 102 103 struct twl_clks_data { 104 struct clk_init_data init; 105 u8 base; 106 }; 107 108 static const struct twl_clks_data twl6032_clks[] = { 109 { 110 .init = { 111 .name = "clk32kg", 112 .ops = &twl6032_clks_ops, 113 .flags = CLK_IGNORE_UNUSED, 114 }, 115 .base = 0x8C, 116 }, 117 { 118 .init = { 119 .name = "clk32kaudio", 120 .ops = &twl6032_clks_ops, 121 .flags = CLK_IGNORE_UNUSED, 122 }, 123 .base = 0x8F, 124 }, 125 { 126 /* sentinel */ 127 } 128 }; 129 130 static int twl_clks_probe(struct platform_device *pdev) 131 { 132 struct clk_hw_onecell_data *clk_data; 133 const struct twl_clks_data *hw_data; 134 135 struct twl_clock_info *cinfo; 136 int ret; 137 int i; 138 int count; 139 140 hw_data = twl6032_clks; 141 for (count = 0; hw_data[count].init.name; count++) 142 ; 143 144 clk_data = devm_kzalloc(&pdev->dev, 145 struct_size(clk_data, hws, count), 146 GFP_KERNEL); 147 if (!clk_data) 148 return -ENOMEM; 149 150 clk_data->num = count; 151 cinfo = devm_kcalloc(&pdev->dev, count, sizeof(*cinfo), GFP_KERNEL); 152 if (!cinfo) 153 return -ENOMEM; 154 155 for (i = 0; i < count; i++) { 156 cinfo[i].base = hw_data[i].base; 157 cinfo[i].dev = &pdev->dev; 158 cinfo[i].hw.init = &hw_data[i].init; 159 ret = devm_clk_hw_register(&pdev->dev, &cinfo[i].hw); 160 if (ret) { 161 return dev_err_probe(&pdev->dev, ret, 162 "Fail to register clock %s\n", 163 hw_data[i].init.name); 164 } 165 clk_data->hws[i] = &cinfo[i].hw; 166 } 167 168 ret = devm_of_clk_add_hw_provider(&pdev->dev, 169 of_clk_hw_onecell_get, clk_data); 170 if (ret < 0) 171 return dev_err_probe(&pdev->dev, ret, 172 "Fail to add clock driver\n"); 173 174 return 0; 175 } 176 177 static const struct platform_device_id twl_clks_id[] = { 178 { 179 .name = "twl6032-clk", 180 }, { 181 /* sentinel */ 182 } 183 }; 184 MODULE_DEVICE_TABLE(platform, twl_clks_id); 185 186 static struct platform_driver twl_clks_driver = { 187 .driver = { 188 .name = "twl-clk", 189 }, 190 .probe = twl_clks_probe, 191 .id_table = twl_clks_id, 192 }; 193 194 module_platform_driver(twl_clks_driver); 195 196 MODULE_DESCRIPTION("Clock driver for TWL Series Devices"); 197 MODULE_LICENSE("GPL"); 198