diff options
Diffstat (limited to 'drivers/w1/masters/matrox_w1.c')
-rw-r--r-- | drivers/w1/masters/matrox_w1.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/drivers/w1/masters/matrox_w1.c b/drivers/w1/masters/matrox_w1.c new file mode 100644 index 000000000000..591809cbbb97 --- /dev/null +++ b/drivers/w1/masters/matrox_w1.c | |||
@@ -0,0 +1,247 @@ | |||
1 | /* | ||
2 | * matrox_w1.c | ||
3 | * | ||
4 | * Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru> | ||
5 | * | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include <asm/types.h> | ||
23 | #include <asm/atomic.h> | ||
24 | #include <asm/io.h> | ||
25 | |||
26 | #include <linux/delay.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/list.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/spinlock.h> | ||
32 | #include <linux/timer.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/pci_ids.h> | ||
35 | #include <linux/pci.h> | ||
36 | #include <linux/timer.h> | ||
37 | |||
38 | #include "../w1.h" | ||
39 | #include "../w1_int.h" | ||
40 | #include "../w1_log.h" | ||
41 | |||
42 | MODULE_LICENSE("GPL"); | ||
43 | MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>"); | ||
44 | MODULE_DESCRIPTION("Driver for transport(Dallas 1-wire prtocol) over VGA DDC(matrox gpio)."); | ||
45 | |||
46 | static struct pci_device_id matrox_w1_tbl[] = { | ||
47 | { PCI_DEVICE(PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_G400) }, | ||
48 | { }, | ||
49 | }; | ||
50 | MODULE_DEVICE_TABLE(pci, matrox_w1_tbl); | ||
51 | |||
52 | static int __devinit matrox_w1_probe(struct pci_dev *, const struct pci_device_id *); | ||
53 | static void __devexit matrox_w1_remove(struct pci_dev *); | ||
54 | |||
55 | static struct pci_driver matrox_w1_pci_driver = { | ||
56 | .name = "matrox_w1", | ||
57 | .id_table = matrox_w1_tbl, | ||
58 | .probe = matrox_w1_probe, | ||
59 | .remove = __devexit_p(matrox_w1_remove), | ||
60 | }; | ||
61 | |||
62 | /* | ||
63 | * Matrox G400 DDC registers. | ||
64 | */ | ||
65 | |||
66 | #define MATROX_G400_DDC_CLK (1<<4) | ||
67 | #define MATROX_G400_DDC_DATA (1<<1) | ||
68 | |||
69 | #define MATROX_BASE 0x3C00 | ||
70 | #define MATROX_STATUS 0x1e14 | ||
71 | |||
72 | #define MATROX_PORT_INDEX_OFFSET 0x00 | ||
73 | #define MATROX_PORT_DATA_OFFSET 0x0A | ||
74 | |||
75 | #define MATROX_GET_CONTROL 0x2A | ||
76 | #define MATROX_GET_DATA 0x2B | ||
77 | #define MATROX_CURSOR_CTL 0x06 | ||
78 | |||
79 | struct matrox_device | ||
80 | { | ||
81 | void __iomem *base_addr; | ||
82 | void __iomem *port_index; | ||
83 | void __iomem *port_data; | ||
84 | u8 data_mask; | ||
85 | |||
86 | unsigned long phys_addr; | ||
87 | void __iomem *virt_addr; | ||
88 | unsigned long found; | ||
89 | |||
90 | struct w1_bus_master *bus_master; | ||
91 | }; | ||
92 | |||
93 | static u8 matrox_w1_read_ddc_bit(void *); | ||
94 | static void matrox_w1_write_ddc_bit(void *, u8); | ||
95 | |||
96 | /* | ||
97 | * These functions read and write DDC Data bit. | ||
98 | * | ||
99 | * Using tristate pins, since i can't find any open-drain pin in whole motherboard. | ||
100 | * Unfortunately we can't connect to Intel's 82801xx IO controller | ||
101 | * since we don't know motherboard schema, wich has pretty unused(may be not) GPIO. | ||
102 | * | ||
103 | * I've heard that PIIX also has open drain pin. | ||
104 | * | ||
105 | * Port mapping. | ||
106 | */ | ||
107 | static __inline__ u8 matrox_w1_read_reg(struct matrox_device *dev, u8 reg) | ||
108 | { | ||
109 | u8 ret; | ||
110 | |||
111 | writeb(reg, dev->port_index); | ||
112 | ret = readb(dev->port_data); | ||
113 | barrier(); | ||
114 | |||
115 | return ret; | ||
116 | } | ||
117 | |||
118 | static __inline__ void matrox_w1_write_reg(struct matrox_device *dev, u8 reg, u8 val) | ||
119 | { | ||
120 | writeb(reg, dev->port_index); | ||
121 | writeb(val, dev->port_data); | ||
122 | wmb(); | ||
123 | } | ||
124 | |||
125 | static void matrox_w1_write_ddc_bit(void *data, u8 bit) | ||
126 | { | ||
127 | u8 ret; | ||
128 | struct matrox_device *dev = data; | ||
129 | |||
130 | if (bit) | ||
131 | bit = 0; | ||
132 | else | ||
133 | bit = dev->data_mask; | ||
134 | |||
135 | ret = matrox_w1_read_reg(dev, MATROX_GET_CONTROL); | ||
136 | matrox_w1_write_reg(dev, MATROX_GET_CONTROL, ((ret & ~dev->data_mask) | bit)); | ||
137 | matrox_w1_write_reg(dev, MATROX_GET_DATA, 0x00); | ||
138 | } | ||
139 | |||
140 | static u8 matrox_w1_read_ddc_bit(void *data) | ||
141 | { | ||
142 | u8 ret; | ||
143 | struct matrox_device *dev = data; | ||
144 | |||
145 | ret = matrox_w1_read_reg(dev, MATROX_GET_DATA); | ||
146 | |||
147 | return ret; | ||
148 | } | ||
149 | |||
150 | static void matrox_w1_hw_init(struct matrox_device *dev) | ||
151 | { | ||
152 | matrox_w1_write_reg(dev, MATROX_GET_DATA, 0xFF); | ||
153 | matrox_w1_write_reg(dev, MATROX_GET_CONTROL, 0x00); | ||
154 | } | ||
155 | |||
156 | static int __devinit matrox_w1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | ||
157 | { | ||
158 | struct matrox_device *dev; | ||
159 | int err; | ||
160 | |||
161 | assert(pdev != NULL); | ||
162 | assert(ent != NULL); | ||
163 | |||
164 | if (pdev->vendor != PCI_VENDOR_ID_MATROX || pdev->device != PCI_DEVICE_ID_MATROX_G400) | ||
165 | return -ENODEV; | ||
166 | |||
167 | dev = kmalloc(sizeof(struct matrox_device) + | ||
168 | sizeof(struct w1_bus_master), GFP_KERNEL); | ||
169 | if (!dev) { | ||
170 | dev_err(&pdev->dev, | ||
171 | "%s: Failed to create new matrox_device object.\n", | ||
172 | __func__); | ||
173 | return -ENOMEM; | ||
174 | } | ||
175 | |||
176 | memset(dev, 0, sizeof(struct matrox_device) + sizeof(struct w1_bus_master)); | ||
177 | |||
178 | dev->bus_master = (struct w1_bus_master *)(dev + 1); | ||
179 | |||
180 | /* | ||
181 | * True for G400, for some other we need resource 0, see drivers/video/matrox/matroxfb_base.c | ||
182 | */ | ||
183 | |||
184 | dev->phys_addr = pci_resource_start(pdev, 1); | ||
185 | |||
186 | dev->virt_addr = ioremap_nocache(dev->phys_addr, 16384); | ||
187 | if (!dev->virt_addr) { | ||
188 | dev_err(&pdev->dev, "%s: failed to ioremap(0x%lx, %d).\n", | ||
189 | __func__, dev->phys_addr, 16384); | ||
190 | err = -EIO; | ||
191 | goto err_out_free_device; | ||
192 | } | ||
193 | |||
194 | dev->base_addr = dev->virt_addr + MATROX_BASE; | ||
195 | dev->port_index = dev->base_addr + MATROX_PORT_INDEX_OFFSET; | ||
196 | dev->port_data = dev->base_addr + MATROX_PORT_DATA_OFFSET; | ||
197 | dev->data_mask = (MATROX_G400_DDC_DATA); | ||
198 | |||
199 | matrox_w1_hw_init(dev); | ||
200 | |||
201 | dev->bus_master->data = dev; | ||
202 | dev->bus_master->read_bit = &matrox_w1_read_ddc_bit; | ||
203 | dev->bus_master->write_bit = &matrox_w1_write_ddc_bit; | ||
204 | |||
205 | err = w1_add_master_device(dev->bus_master); | ||
206 | if (err) | ||
207 | goto err_out_free_device; | ||
208 | |||
209 | pci_set_drvdata(pdev, dev); | ||
210 | |||
211 | dev->found = 1; | ||
212 | |||
213 | dev_info(&pdev->dev, "Matrox G400 GPIO transport layer for 1-wire.\n"); | ||
214 | |||
215 | return 0; | ||
216 | |||
217 | err_out_free_device: | ||
218 | kfree(dev); | ||
219 | |||
220 | return err; | ||
221 | } | ||
222 | |||
223 | static void __devexit matrox_w1_remove(struct pci_dev *pdev) | ||
224 | { | ||
225 | struct matrox_device *dev = pci_get_drvdata(pdev); | ||
226 | |||
227 | assert(dev != NULL); | ||
228 | |||
229 | if (dev->found) { | ||
230 | w1_remove_master_device(dev->bus_master); | ||
231 | iounmap(dev->virt_addr); | ||
232 | } | ||
233 | kfree(dev); | ||
234 | } | ||
235 | |||
236 | static int __init matrox_w1_init(void) | ||
237 | { | ||
238 | return pci_register_driver(&matrox_w1_pci_driver); | ||
239 | } | ||
240 | |||
241 | static void __exit matrox_w1_fini(void) | ||
242 | { | ||
243 | pci_unregister_driver(&matrox_w1_pci_driver); | ||
244 | } | ||
245 | |||
246 | module_init(matrox_w1_init); | ||
247 | module_exit(matrox_w1_fini); | ||