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