1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Layerscape SFP driver |
4 | * |
5 | * Copyright (c) 2022 Michael Walle <michael@walle.cc> |
6 | * |
7 | */ |
8 | |
9 | #include <linux/device.h> |
10 | #include <linux/io.h> |
11 | #include <linux/mod_devicetable.h> |
12 | #include <linux/module.h> |
13 | #include <linux/nvmem-provider.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/property.h> |
16 | #include <linux/regmap.h> |
17 | |
18 | #define LAYERSCAPE_SFP_OTP_OFFSET 0x0200 |
19 | |
20 | struct layerscape_sfp_priv { |
21 | struct regmap *regmap; |
22 | }; |
23 | |
24 | struct layerscape_sfp_data { |
25 | int size; |
26 | enum regmap_endian endian; |
27 | }; |
28 | |
29 | static int layerscape_sfp_read(void *context, unsigned int offset, void *val, |
30 | size_t bytes) |
31 | { |
32 | struct layerscape_sfp_priv *priv = context; |
33 | |
34 | return regmap_bulk_read(map: priv->regmap, |
35 | LAYERSCAPE_SFP_OTP_OFFSET + offset, val, |
36 | val_count: bytes / 4); |
37 | } |
38 | |
39 | static struct nvmem_config layerscape_sfp_nvmem_config = { |
40 | .name = "fsl-sfp" , |
41 | .reg_read = layerscape_sfp_read, |
42 | .word_size = 4, |
43 | .stride = 4, |
44 | }; |
45 | |
46 | static int layerscape_sfp_probe(struct platform_device *pdev) |
47 | { |
48 | const struct layerscape_sfp_data *data; |
49 | struct layerscape_sfp_priv *priv; |
50 | struct nvmem_device *nvmem; |
51 | struct regmap_config config = { 0 }; |
52 | void __iomem *base; |
53 | |
54 | priv = devm_kzalloc(dev: &pdev->dev, size: sizeof(*priv), GFP_KERNEL); |
55 | if (!priv) |
56 | return -ENOMEM; |
57 | |
58 | base = devm_platform_ioremap_resource(pdev, index: 0); |
59 | if (IS_ERR(ptr: base)) |
60 | return PTR_ERR(ptr: base); |
61 | |
62 | data = device_get_match_data(dev: &pdev->dev); |
63 | config.reg_bits = 32; |
64 | config.reg_stride = 4; |
65 | config.val_bits = 32; |
66 | config.val_format_endian = data->endian; |
67 | config.max_register = LAYERSCAPE_SFP_OTP_OFFSET + data->size - 4; |
68 | priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, &config); |
69 | if (IS_ERR(ptr: priv->regmap)) |
70 | return PTR_ERR(ptr: priv->regmap); |
71 | |
72 | layerscape_sfp_nvmem_config.size = data->size; |
73 | layerscape_sfp_nvmem_config.dev = &pdev->dev; |
74 | layerscape_sfp_nvmem_config.priv = priv; |
75 | |
76 | nvmem = devm_nvmem_register(dev: &pdev->dev, cfg: &layerscape_sfp_nvmem_config); |
77 | |
78 | return PTR_ERR_OR_ZERO(ptr: nvmem); |
79 | } |
80 | |
81 | static const struct layerscape_sfp_data ls1021a_data = { |
82 | .size = 0x88, |
83 | .endian = REGMAP_ENDIAN_BIG, |
84 | }; |
85 | |
86 | static const struct layerscape_sfp_data ls1028a_data = { |
87 | .size = 0x88, |
88 | .endian = REGMAP_ENDIAN_LITTLE, |
89 | }; |
90 | |
91 | static const struct of_device_id layerscape_sfp_dt_ids[] = { |
92 | { .compatible = "fsl,ls1021a-sfp" , .data = &ls1021a_data }, |
93 | { .compatible = "fsl,ls1028a-sfp" , .data = &ls1028a_data }, |
94 | {}, |
95 | }; |
96 | MODULE_DEVICE_TABLE(of, layerscape_sfp_dt_ids); |
97 | |
98 | static struct platform_driver layerscape_sfp_driver = { |
99 | .probe = layerscape_sfp_probe, |
100 | .driver = { |
101 | .name = "layerscape_sfp" , |
102 | .of_match_table = layerscape_sfp_dt_ids, |
103 | }, |
104 | }; |
105 | module_platform_driver(layerscape_sfp_driver); |
106 | |
107 | MODULE_AUTHOR("Michael Walle <michael@walle.cc>" ); |
108 | MODULE_DESCRIPTION("Layerscape Security Fuse Processor driver" ); |
109 | MODULE_LICENSE("GPL" ); |
110 | |