1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * sgi_w1.c - w1 master driver for one wire support in SGI ASICs |
4 | */ |
5 | |
6 | #include <linux/clk.h> |
7 | #include <linux/delay.h> |
8 | #include <linux/io.h> |
9 | #include <linux/jiffies.h> |
10 | #include <linux/module.h> |
11 | #include <linux/mod_devicetable.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/platform_data/sgi-w1.h> |
14 | |
15 | #include <linux/w1.h> |
16 | |
17 | #define MCR_RD_DATA BIT(0) |
18 | #define MCR_DONE BIT(1) |
19 | |
20 | #define MCR_PACK(pulse, sample) (((pulse) << 10) | ((sample) << 2)) |
21 | |
22 | struct sgi_w1_device { |
23 | u32 __iomem *mcr; |
24 | struct w1_bus_master bus_master; |
25 | char dev_id[64]; |
26 | }; |
27 | |
28 | static u8 sgi_w1_wait(u32 __iomem *mcr) |
29 | { |
30 | u32 mcr_val; |
31 | |
32 | do { |
33 | mcr_val = readl(addr: mcr); |
34 | } while (!(mcr_val & MCR_DONE)); |
35 | |
36 | return (mcr_val & MCR_RD_DATA) ? 1 : 0; |
37 | } |
38 | |
39 | /* |
40 | * this is the low level routine to |
41 | * reset the device on the One Wire interface |
42 | * on the hardware |
43 | */ |
44 | static u8 sgi_w1_reset_bus(void *data) |
45 | { |
46 | struct sgi_w1_device *dev = data; |
47 | u8 ret; |
48 | |
49 | writel(MCR_PACK(520, 65), addr: dev->mcr); |
50 | ret = sgi_w1_wait(mcr: dev->mcr); |
51 | udelay(500); /* recovery time */ |
52 | return ret; |
53 | } |
54 | |
55 | /* |
56 | * this is the low level routine to read/write a bit on the One Wire |
57 | * interface on the hardware. It does write 0 if parameter bit is set |
58 | * to 0, otherwise a write 1/read. |
59 | */ |
60 | static u8 sgi_w1_touch_bit(void *data, u8 bit) |
61 | { |
62 | struct sgi_w1_device *dev = data; |
63 | u8 ret; |
64 | |
65 | if (bit) |
66 | writel(MCR_PACK(6, 13), addr: dev->mcr); |
67 | else |
68 | writel(MCR_PACK(80, 30), addr: dev->mcr); |
69 | |
70 | ret = sgi_w1_wait(mcr: dev->mcr); |
71 | if (bit) |
72 | udelay(100); /* recovery */ |
73 | return ret; |
74 | } |
75 | |
76 | static int sgi_w1_probe(struct platform_device *pdev) |
77 | { |
78 | struct sgi_w1_device *sdev; |
79 | struct sgi_w1_platform_data *pdata; |
80 | |
81 | sdev = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct sgi_w1_device), |
82 | GFP_KERNEL); |
83 | if (!sdev) |
84 | return -ENOMEM; |
85 | |
86 | sdev->mcr = devm_platform_ioremap_resource(pdev, index: 0); |
87 | if (IS_ERR(ptr: sdev->mcr)) |
88 | return PTR_ERR(ptr: sdev->mcr); |
89 | |
90 | sdev->bus_master.data = sdev; |
91 | sdev->bus_master.reset_bus = sgi_w1_reset_bus; |
92 | sdev->bus_master.touch_bit = sgi_w1_touch_bit; |
93 | |
94 | pdata = dev_get_platdata(dev: &pdev->dev); |
95 | if (pdata) { |
96 | strscpy(sdev->dev_id, pdata->dev_id, sizeof(sdev->dev_id)); |
97 | sdev->bus_master.dev_id = sdev->dev_id; |
98 | } |
99 | |
100 | platform_set_drvdata(pdev, data: sdev); |
101 | |
102 | return w1_add_master_device(master: &sdev->bus_master); |
103 | } |
104 | |
105 | /* |
106 | * disassociate the w1 device from the driver |
107 | */ |
108 | static void sgi_w1_remove(struct platform_device *pdev) |
109 | { |
110 | struct sgi_w1_device *sdev = platform_get_drvdata(pdev); |
111 | |
112 | w1_remove_master_device(master: &sdev->bus_master); |
113 | } |
114 | |
115 | static struct platform_driver sgi_w1_driver = { |
116 | .driver = { |
117 | .name = "sgi_w1" , |
118 | }, |
119 | .probe = sgi_w1_probe, |
120 | .remove_new = sgi_w1_remove, |
121 | }; |
122 | module_platform_driver(sgi_w1_driver); |
123 | |
124 | MODULE_LICENSE("GPL" ); |
125 | MODULE_AUTHOR("Thomas Bogendoerfer" ); |
126 | MODULE_DESCRIPTION("Driver for One-Wire IP in SGI ASICs" ); |
127 | |