File Index
Files and directories
Sign in
FILE

sec-acpm.c

File preview with syntax highlighting.

linux-6.19.14/drivers/mfd/sec-acpm.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright 2020 Google Inc
  4 * Copyright 2025 Linaro Ltd.
  5 *
  6 * Samsung S2MPG1x ACPM driver
  7 */
  8
  9#include <linux/array_size.h>
 10#include <linux/bitops.h>
 11#include <linux/device.h>
 12#include <linux/firmware/samsung/exynos-acpm-protocol.h>
 13#include <linux/mfd/samsung/core.h>
 14#include <linux/mfd/samsung/rtc.h>
 15#include <linux/mfd/samsung/s2mpg10.h>
 16#include <linux/mod_devicetable.h>
 17#include <linux/module.h>
 18#include <linux/of.h>
 19#include <linux/platform_device.h>
 20#include <linux/pm.h>
 21#include <linux/property.h>
 22#include <linux/regmap.h>
 23#include "sec-core.h"
 24
 25#define ACPM_ADDR_BITS       8
 26#define ACPM_MAX_BULK_DATA   8
 27
 28struct sec_pmic_acpm_platform_data {
 29	int device_type;
 30
 31	unsigned int acpm_chan_id;
 32	u8 speedy_channel;
 33
 34	const struct regmap_config *regmap_cfg_common;
 35	const struct regmap_config *regmap_cfg_pmic;
 36	const struct regmap_config *regmap_cfg_rtc;
 37	const struct regmap_config *regmap_cfg_meter;
 38};
 39
 40static const struct regmap_range s2mpg10_common_registers[] = {
 41	regmap_reg_range(0x00, 0x02), /* CHIP_ID_M, INT, INT_MASK */
 42	regmap_reg_range(0x0a, 0x0c), /* Speedy control */
 43	regmap_reg_range(0x1a, 0x2a), /* Debug */
 44};
 45
 46static const struct regmap_range s2mpg10_common_ro_registers[] = {
 47	regmap_reg_range(0x00, 0x01), /* CHIP_ID_M, INT */
 48	regmap_reg_range(0x28, 0x2a), /* Debug */
 49};
 50
 51static const struct regmap_range s2mpg10_common_nonvolatile_registers[] = {
 52	regmap_reg_range(0x00, 0x00), /* CHIP_ID_M */
 53	regmap_reg_range(0x02, 0x02), /* INT_MASK */
 54	regmap_reg_range(0x0a, 0x0c), /* Speedy control */
 55};
 56
 57static const struct regmap_range s2mpg10_common_precious_registers[] = {
 58	regmap_reg_range(0x01, 0x01), /* INT */
 59};
 60
 61static const struct regmap_access_table s2mpg10_common_wr_table = {
 62	.yes_ranges = s2mpg10_common_registers,
 63	.n_yes_ranges = ARRAY_SIZE(s2mpg10_common_registers),
 64	.no_ranges = s2mpg10_common_ro_registers,
 65	.n_no_ranges = ARRAY_SIZE(s2mpg10_common_ro_registers),
 66};
 67
 68static const struct regmap_access_table s2mpg10_common_rd_table = {
 69	.yes_ranges = s2mpg10_common_registers,
 70	.n_yes_ranges = ARRAY_SIZE(s2mpg10_common_registers),
 71};
 72
 73static const struct regmap_access_table s2mpg10_common_volatile_table = {
 74	.no_ranges = s2mpg10_common_nonvolatile_registers,
 75	.n_no_ranges = ARRAY_SIZE(s2mpg10_common_nonvolatile_registers),
 76};
 77
 78static const struct regmap_access_table s2mpg10_common_precious_table = {
 79	.yes_ranges = s2mpg10_common_precious_registers,
 80	.n_yes_ranges = ARRAY_SIZE(s2mpg10_common_precious_registers),
 81};
 82
 83static const struct regmap_config s2mpg10_regmap_config_common = {
 84	.name = "common",
 85	.reg_bits = ACPM_ADDR_BITS,
 86	.val_bits = 8,
 87	.max_register = S2MPG10_COMMON_SPD_DEBUG4,
 88	.wr_table = &s2mpg10_common_wr_table,
 89	.rd_table = &s2mpg10_common_rd_table,
 90	.volatile_table = &s2mpg10_common_volatile_table,
 91	.precious_table = &s2mpg10_common_precious_table,
 92	.num_reg_defaults_raw = S2MPG10_COMMON_SPD_DEBUG4 + 1,
 93	.cache_type = REGCACHE_FLAT,
 94};
 95
 96static const struct regmap_range s2mpg10_pmic_registers[] = {
 97	regmap_reg_range(0x00, 0xf6), /* All PMIC registers */
 98};
 99
100static const struct regmap_range s2mpg10_pmic_ro_registers[] = {
101	regmap_reg_range(0x00, 0x05), /* INTx */
102	regmap_reg_range(0x0c, 0x0f), /* STATUSx PWRONSRC OFFSRC */
103	regmap_reg_range(0xc7, 0xc7), /* GPIO input */
104};
105
106static const struct regmap_range s2mpg10_pmic_nonvolatile_registers[] = {
107	regmap_reg_range(0x06, 0x0b), /* INTxM */
108};
109
110static const struct regmap_range s2mpg10_pmic_precious_registers[] = {
111	regmap_reg_range(0x00, 0x05), /* INTx */
112};
113
114static const struct regmap_access_table s2mpg10_pmic_wr_table = {
115	.yes_ranges = s2mpg10_pmic_registers,
116	.n_yes_ranges = ARRAY_SIZE(s2mpg10_pmic_registers),
117	.no_ranges = s2mpg10_pmic_ro_registers,
118	.n_no_ranges = ARRAY_SIZE(s2mpg10_pmic_ro_registers),
119};
120
121static const struct regmap_access_table s2mpg10_pmic_rd_table = {
122	.yes_ranges = s2mpg10_pmic_registers,
123	.n_yes_ranges = ARRAY_SIZE(s2mpg10_pmic_registers),
124};
125
126static const struct regmap_access_table s2mpg10_pmic_volatile_table = {
127	.no_ranges = s2mpg10_pmic_nonvolatile_registers,
128	.n_no_ranges = ARRAY_SIZE(s2mpg10_pmic_nonvolatile_registers),
129};
130
131static const struct regmap_access_table s2mpg10_pmic_precious_table = {
132	.yes_ranges = s2mpg10_pmic_precious_registers,
133	.n_yes_ranges = ARRAY_SIZE(s2mpg10_pmic_precious_registers),
134};
135
136static const struct regmap_config s2mpg10_regmap_config_pmic = {
137	.name = "pmic",
138	.reg_bits = ACPM_ADDR_BITS,
139	.val_bits = 8,
140	.max_register = S2MPG10_PMIC_LDO_SENSE4,
141	.wr_table = &s2mpg10_pmic_wr_table,
142	.rd_table = &s2mpg10_pmic_rd_table,
143	.volatile_table = &s2mpg10_pmic_volatile_table,
144	.precious_table = &s2mpg10_pmic_precious_table,
145	.num_reg_defaults_raw = S2MPG10_PMIC_LDO_SENSE4 + 1,
146	.cache_type = REGCACHE_FLAT,
147};
148
149static const struct regmap_range s2mpg10_rtc_registers[] = {
150	regmap_reg_range(0x00, 0x2b), /* All RTC registers */
151};
152
153static const struct regmap_range s2mpg10_rtc_volatile_registers[] = {
154	regmap_reg_range(0x01, 0x01), /* RTC_UPDATE */
155	regmap_reg_range(0x05, 0x0c), /* Time / date */
156};
157
158static const struct regmap_access_table s2mpg10_rtc_rd_table = {
159	.yes_ranges = s2mpg10_rtc_registers,
160	.n_yes_ranges = ARRAY_SIZE(s2mpg10_rtc_registers),
161};
162
163static const struct regmap_access_table s2mpg10_rtc_volatile_table = {
164	.yes_ranges = s2mpg10_rtc_volatile_registers,
165	.n_yes_ranges = ARRAY_SIZE(s2mpg10_rtc_volatile_registers),
166};
167
168static const struct regmap_config s2mpg10_regmap_config_rtc = {
169	.name = "rtc",
170	.reg_bits = ACPM_ADDR_BITS,
171	.val_bits = 8,
172	.max_register = S2MPG10_RTC_OSC_CTRL,
173	.rd_table = &s2mpg10_rtc_rd_table,
174	.volatile_table = &s2mpg10_rtc_volatile_table,
175	.num_reg_defaults_raw = S2MPG10_RTC_OSC_CTRL + 1,
176	.cache_type = REGCACHE_FLAT,
177};
178
179static const struct regmap_range s2mpg10_meter_registers[] = {
180	regmap_reg_range(0x00, 0x21), /* Meter config */
181	regmap_reg_range(0x40, 0x8a), /* Meter data */
182	regmap_reg_range(0xee, 0xee), /* Offset */
183	regmap_reg_range(0xf1, 0xf1), /* Trim */
184};
185
186static const struct regmap_range s2mpg10_meter_ro_registers[] = {
187	regmap_reg_range(0x40, 0x8a), /* Meter data */
188};
189
190static const struct regmap_access_table s2mpg10_meter_wr_table = {
191	.yes_ranges = s2mpg10_meter_registers,
192	.n_yes_ranges = ARRAY_SIZE(s2mpg10_meter_registers),
193	.no_ranges = s2mpg10_meter_ro_registers,
194	.n_no_ranges = ARRAY_SIZE(s2mpg10_meter_ro_registers),
195};
196
197static const struct regmap_access_table s2mpg10_meter_rd_table = {
198	.yes_ranges = s2mpg10_meter_registers,
199	.n_yes_ranges = ARRAY_SIZE(s2mpg10_meter_registers),
200};
201
202static const struct regmap_access_table s2mpg10_meter_volatile_table = {
203	.yes_ranges = s2mpg10_meter_ro_registers,
204	.n_yes_ranges = ARRAY_SIZE(s2mpg10_meter_ro_registers),
205};
206
207static const struct regmap_config s2mpg10_regmap_config_meter = {
208	.name = "meter",
209	.reg_bits = ACPM_ADDR_BITS,
210	.val_bits = 8,
211	.max_register = S2MPG10_METER_BUCK_METER_TRIM3,
212	.wr_table = &s2mpg10_meter_wr_table,
213	.rd_table = &s2mpg10_meter_rd_table,
214	.volatile_table = &s2mpg10_meter_volatile_table,
215	.num_reg_defaults_raw = S2MPG10_METER_BUCK_METER_TRIM3 + 1,
216	.cache_type = REGCACHE_FLAT,
217};
218
219struct sec_pmic_acpm_shared_bus_context {
220	const struct acpm_handle *acpm;
221	unsigned int acpm_chan_id;
222	u8 speedy_channel;
223};
224
225enum sec_pmic_acpm_accesstype {
226	SEC_PMIC_ACPM_ACCESSTYPE_COMMON = 0x00,
227	SEC_PMIC_ACPM_ACCESSTYPE_PMIC = 0x01,
228	SEC_PMIC_ACPM_ACCESSTYPE_RTC = 0x02,
229	SEC_PMIC_ACPM_ACCESSTYPE_METER = 0x0a,
230	SEC_PMIC_ACPM_ACCESSTYPE_WLWP = 0x0b,
231	SEC_PMIC_ACPM_ACCESSTYPE_TRIM = 0x0f,
232};
233
234struct sec_pmic_acpm_bus_context {
235	struct sec_pmic_acpm_shared_bus_context *shared;
236	enum sec_pmic_acpm_accesstype type;
237};
238
239static int sec_pmic_acpm_bus_write(void *context, const void *data,
240				   size_t count)
241{
242	struct sec_pmic_acpm_bus_context *ctx = context;
243	const struct acpm_handle *acpm = ctx->shared->acpm;
244	const struct acpm_pmic_ops *pmic_ops = &acpm->ops.pmic_ops;
245	size_t val_count = count - BITS_TO_BYTES(ACPM_ADDR_BITS);
246	const u8 *d = data;
247	const u8 *vals = &d[BITS_TO_BYTES(ACPM_ADDR_BITS)];
248	u8 reg;
249
250	if (val_count < 1 || val_count > ACPM_MAX_BULK_DATA)
251		return -EINVAL;
252
253	reg = d[0];
254
255	return pmic_ops->bulk_write(acpm, ctx->shared->acpm_chan_id, ctx->type, reg,
256				    ctx->shared->speedy_channel, val_count, vals);
257}
258
259static int sec_pmic_acpm_bus_read(void *context, const void *reg_buf, size_t reg_size,
260				  void *val_buf, size_t val_size)
261{
262	struct sec_pmic_acpm_bus_context *ctx = context;
263	const struct acpm_handle *acpm = ctx->shared->acpm;
264	const struct acpm_pmic_ops *pmic_ops = &acpm->ops.pmic_ops;
265	const u8 *r = reg_buf;
266	u8 reg;
267
268	if (reg_size != BITS_TO_BYTES(ACPM_ADDR_BITS) || !val_size ||
269	    val_size > ACPM_MAX_BULK_DATA)
270		return -EINVAL;
271
272	reg = r[0];
273
274	return pmic_ops->bulk_read(acpm, ctx->shared->acpm_chan_id, ctx->type, reg,
275				   ctx->shared->speedy_channel, val_size, val_buf);
276}
277
278static int sec_pmic_acpm_bus_reg_update_bits(void *context, unsigned int reg, unsigned int mask,
279					     unsigned int val)
280{
281	struct sec_pmic_acpm_bus_context *ctx = context;
282	const struct acpm_handle *acpm = ctx->shared->acpm;
283	const struct acpm_pmic_ops *pmic_ops = &acpm->ops.pmic_ops;
284
285	return pmic_ops->update_reg(acpm, ctx->shared->acpm_chan_id, ctx->type, reg & 0xff,
286				    ctx->shared->speedy_channel, val, mask);
287}
288
289static const struct regmap_bus sec_pmic_acpm_regmap_bus = {
290	.write = sec_pmic_acpm_bus_write,
291	.read = sec_pmic_acpm_bus_read,
292	.reg_update_bits = sec_pmic_acpm_bus_reg_update_bits,
293	.max_raw_read = ACPM_MAX_BULK_DATA,
294	.max_raw_write = ACPM_MAX_BULK_DATA,
295};
296
297static struct regmap *sec_pmic_acpm_regmap_init(struct device *dev,
298						struct sec_pmic_acpm_shared_bus_context *shared_ctx,
299						enum sec_pmic_acpm_accesstype type,
300						const struct regmap_config *cfg, bool do_attach)
301{
302	struct sec_pmic_acpm_bus_context *ctx;
303	struct regmap *regmap;
304
305	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
306	if (!ctx)
307		return ERR_PTR(-ENOMEM);
308
309	ctx->shared = shared_ctx;
310	ctx->type = type;
311
312	regmap = devm_regmap_init(dev, &sec_pmic_acpm_regmap_bus, ctx, cfg);
313	if (IS_ERR(regmap))
314		return dev_err_cast_probe(dev, regmap, "regmap init (%s) failed\n", cfg->name);
315
316	if (do_attach) {
317		int ret;
318
319		ret = regmap_attach_dev(dev, regmap, cfg);
320		if (ret)
321			return dev_err_ptr_probe(dev, ret, "regmap attach (%s) failed\n",
322						 cfg->name);
323	}
324
325	return regmap;
326}
327
328static int sec_pmic_acpm_probe(struct platform_device *pdev)
329{
330	struct regmap *regmap_common, *regmap_pmic, *regmap;
331	const struct sec_pmic_acpm_platform_data *pdata;
332	struct sec_pmic_acpm_shared_bus_context *shared_ctx;
333	const struct acpm_handle *acpm;
334	struct device *dev = &pdev->dev;
335	int ret, irq;
336
337	pdata = device_get_match_data(dev);
338	if (!pdata)
339		return dev_err_probe(dev, -ENODEV, "unsupported device type\n");
340
341	acpm = devm_acpm_get_by_node(dev, dev->parent->of_node);
342	if (IS_ERR(acpm))
343		return dev_err_probe(dev, PTR_ERR(acpm), "failed to get acpm\n");
344
345	irq = platform_get_irq(pdev, 0);
346	if (irq < 0)
347		return irq;
348
349	shared_ctx = devm_kzalloc(dev, sizeof(*shared_ctx), GFP_KERNEL);
350	if (!shared_ctx)
351		return -ENOMEM;
352
353	shared_ctx->acpm = acpm;
354	shared_ctx->acpm_chan_id = pdata->acpm_chan_id;
355	shared_ctx->speedy_channel = pdata->speedy_channel;
356
357	regmap_common = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_COMMON,
358						  pdata->regmap_cfg_common, true);
359	if (IS_ERR(regmap_common))
360		return PTR_ERR(regmap_common);
361
362	regmap_pmic = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_PMIC,
363						pdata->regmap_cfg_pmic, false);
364	if (IS_ERR(regmap_pmic))
365		return PTR_ERR(regmap_pmic);
366
367	regmap = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_RTC,
368					   pdata->regmap_cfg_rtc, true);
369	if (IS_ERR(regmap))
370		return PTR_ERR(regmap);
371
372	regmap = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_METER,
373					   pdata->regmap_cfg_meter, true);
374	if (IS_ERR(regmap))
375		return PTR_ERR(regmap);
376
377	ret = sec_pmic_probe(dev, pdata->device_type, irq, regmap_pmic, NULL);
378	if (ret)
379		return ret;
380
381	if (device_property_read_bool(dev, "wakeup-source"))
382		devm_device_init_wakeup(dev);
383
384	return 0;
385}
386
387static void sec_pmic_acpm_shutdown(struct platform_device *pdev)
388{
389	sec_pmic_shutdown(&pdev->dev);
390}
391
392static const struct sec_pmic_acpm_platform_data s2mpg10_data = {
393	.device_type = S2MPG10,
394	.acpm_chan_id = 2,
395	.speedy_channel = 0,
396	.regmap_cfg_common = &s2mpg10_regmap_config_common,
397	.regmap_cfg_pmic = &s2mpg10_regmap_config_pmic,
398	.regmap_cfg_rtc = &s2mpg10_regmap_config_rtc,
399	.regmap_cfg_meter = &s2mpg10_regmap_config_meter,
400};
401
402static const struct of_device_id sec_pmic_acpm_of_match[] = {
403	{ .compatible = "samsung,s2mpg10-pmic", .data = &s2mpg10_data, },
404	{ },
405};
406MODULE_DEVICE_TABLE(of, sec_pmic_acpm_of_match);
407
408static struct platform_driver sec_pmic_acpm_driver = {
409	.driver = {
410		.name = "sec-pmic-acpm",
411		.pm = pm_sleep_ptr(&sec_pmic_pm_ops),
412		.of_match_table = sec_pmic_acpm_of_match,
413	},
414	.probe = sec_pmic_acpm_probe,
415	.shutdown = sec_pmic_acpm_shutdown,
416};
417module_platform_driver(sec_pmic_acpm_driver);
418
419MODULE_AUTHOR("André Draszik <andre.draszik@linaro.org>");
420MODULE_DESCRIPTION("ACPM driver for the Samsung S2MPG1x");
421MODULE_LICENSE("GPL");
~/ Read-only file preview.