Commit | Line | Data |
---|---|---|
f7845101 MW |
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> | |
943eadbd | 16 | #include <linux/regmap.h> |
f7845101 MW |
17 | |
18 | #define LAYERSCAPE_SFP_OTP_OFFSET 0x0200 | |
19 | ||
20 | struct layerscape_sfp_priv { | |
943eadbd | 21 | struct regmap *regmap; |
f7845101 MW |
22 | }; |
23 | ||
24 | struct layerscape_sfp_data { | |
25 | int size; | |
943eadbd | 26 | enum regmap_endian endian; |
f7845101 MW |
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 | ||
943eadbd SA |
34 | return regmap_bulk_read(priv->regmap, |
35 | LAYERSCAPE_SFP_OTP_OFFSET + offset, val, | |
36 | bytes / 4); | |
f7845101 MW |
37 | } |
38 | ||
39 | static struct nvmem_config layerscape_sfp_nvmem_config = { | |
40 | .name = "fsl-sfp", | |
41 | .reg_read = layerscape_sfp_read, | |
943eadbd SA |
42 | .word_size = 4, |
43 | .stride = 4, | |
f7845101 MW |
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; | |
943eadbd SA |
51 | struct regmap_config config = { 0 }; |
52 | void __iomem *base; | |
f7845101 MW |
53 | |
54 | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | |
55 | if (!priv) | |
56 | return -ENOMEM; | |
57 | ||
943eadbd SA |
58 | base = devm_platform_ioremap_resource(pdev, 0); |
59 | if (IS_ERR(base)) | |
60 | return PTR_ERR(base); | |
f7845101 MW |
61 | |
62 | data = device_get_match_data(&pdev->dev); | |
943eadbd SA |
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(priv->regmap)) | |
70 | return PTR_ERR(priv->regmap); | |
f7845101 MW |
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(&pdev->dev, &layerscape_sfp_nvmem_config); | |
77 | ||
78 | return PTR_ERR_OR_ZERO(nvmem); | |
79 | } | |
80 | ||
33a1c661 SA |
81 | static const struct layerscape_sfp_data ls1021a_data = { |
82 | .size = 0x88, | |
83 | .endian = REGMAP_ENDIAN_BIG, | |
84 | }; | |
85 | ||
f7845101 MW |
86 | static const struct layerscape_sfp_data ls1028a_data = { |
87 | .size = 0x88, | |
943eadbd | 88 | .endian = REGMAP_ENDIAN_LITTLE, |
f7845101 MW |
89 | }; |
90 | ||
91 | static const struct of_device_id layerscape_sfp_dt_ids[] = { | |
33a1c661 | 92 | { .compatible = "fsl,ls1021a-sfp", .data = &ls1021a_data }, |
f7845101 MW |
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"); |