blob: 8adf4abaa0352aff13ba7797456544459c27999f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 i2c-savage4.c - Part of lm_sensors, Linux kernel modules for hardware
3 monitoring
4 Copyright (C) 1998-2003 The LM Sensors Team
5 Alexander Wold <awold@bigfoot.com>
6 Mark D. Studebaker <mdsxyz123@yahoo.com>
7
8 Based on i2c-voodoo3.c.
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23*/
24
25/* This interfaces to the I2C bus of the Savage4 to gain access to
26 the BT869 and possibly other I2C devices. The DDC bus is not
27 yet supported because its register is not memory-mapped.
Linus Torvalds1da177e2005-04-16 15:20:36 -070028*/
29
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/kernel.h>
31#include <linux/module.h>
32#include <linux/init.h>
33#include <linux/pci.h>
34#include <linux/i2c.h>
35#include <linux/i2c-algo-bit.h>
36#include <asm/io.h>
37
Jean Delvarec6e16292007-07-12 14:12:31 +020038/* device IDs */
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#define PCI_CHIP_SAVAGE4 0x8A22
40#define PCI_CHIP_SAVAGE2000 0x9102
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
42#define REG 0xff20 /* Serial Port 1 Register */
43
44/* bit locations in the register */
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#define I2C_ENAB 0x00000020
46#define I2C_SCL_OUT 0x00000001
47#define I2C_SDA_OUT 0x00000002
48#define I2C_SCL_IN 0x00000008
49#define I2C_SDA_IN 0x00000010
50
Linus Torvalds1da177e2005-04-16 15:20:36 -070051/* delays */
52#define CYCLE_DELAY 10
53#define TIMEOUT (HZ / 2)
54
55
56static void __iomem *ioaddr;
57
58/* The sav GPIO registers don't have individual masks for each bit
59 so we always have to read before writing. */
60
61static void bit_savi2c_setscl(void *data, int val)
62{
63 unsigned int r;
64 r = readl(ioaddr + REG);
65 if(val)
66 r |= I2C_SCL_OUT;
67 else
68 r &= ~I2C_SCL_OUT;
69 writel(r, ioaddr + REG);
70 readl(ioaddr + REG); /* flush posted write */
71}
72
73static void bit_savi2c_setsda(void *data, int val)
74{
75 unsigned int r;
76 r = readl(ioaddr + REG);
77 if(val)
78 r |= I2C_SDA_OUT;
79 else
80 r &= ~I2C_SDA_OUT;
81 writel(r, ioaddr + REG);
82 readl(ioaddr + REG); /* flush posted write */
83}
84
85/* The GPIO pins are open drain, so the pins always remain outputs.
86 We rely on the i2c-algo-bit routines to set the pins high before
87 reading the input from other chips. */
88
89static int bit_savi2c_getscl(void *data)
90{
91 return (0 != (readl(ioaddr + REG) & I2C_SCL_IN));
92}
93
94static int bit_savi2c_getsda(void *data)
95{
96 return (0 != (readl(ioaddr + REG) & I2C_SDA_IN));
97}
98
99/* Configures the chip */
100
101static int config_s4(struct pci_dev *dev)
102{
103 unsigned long cadr;
104
105 /* map memory */
106 cadr = dev->resource[0].start;
107 cadr &= PCI_BASE_ADDRESS_MEM_MASK;
108 ioaddr = ioremap_nocache(cadr, 0x0080000);
109 if (ioaddr) {
110 /* writel(0x8160, ioaddr + REG2); */
111 writel(0x00000020, ioaddr + REG);
112 dev_info(&dev->dev, "Using Savage4 at %p\n", ioaddr);
113 return 0;
114 }
115 return -ENODEV;
116}
117
118static struct i2c_algo_bit_data sav_i2c_bit_data = {
119 .setsda = bit_savi2c_setsda,
120 .setscl = bit_savi2c_setscl,
121 .getsda = bit_savi2c_getsda,
122 .getscl = bit_savi2c_getscl,
123 .udelay = CYCLE_DELAY,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 .timeout = TIMEOUT
125};
126
127static struct i2c_adapter savage4_i2c_adapter = {
128 .owner = THIS_MODULE,
Stephen Hemminger9ace5552007-02-13 22:09:01 +0100129 .id = I2C_HW_B_SAVAGE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 .name = "I2C Savage4 adapter",
131 .algo_data = &sav_i2c_bit_data,
132};
133
134static struct pci_device_id savage4_ids[] __devinitdata = {
135 { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE4) },
136 { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE2000) },
137 { 0, }
138};
139
140MODULE_DEVICE_TABLE (pci, savage4_ids);
141
142static int __devinit savage4_probe(struct pci_dev *dev, const struct pci_device_id *id)
143{
144 int retval;
145
146 retval = config_s4(dev);
147 if (retval)
148 return retval;
149
150 /* set up the sysfs linkage to our parent device */
151 savage4_i2c_adapter.dev.parent = &dev->dev;
152
153 return i2c_bit_add_bus(&savage4_i2c_adapter);
154}
155
156static void __devexit savage4_remove(struct pci_dev *dev)
157{
Jean Delvare32697112006-12-10 21:21:33 +0100158 i2c_del_adapter(&savage4_i2c_adapter);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 iounmap(ioaddr);
160}
161
162static struct pci_driver savage4_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 .name = "savage4_smbus",
164 .id_table = savage4_ids,
165 .probe = savage4_probe,
166 .remove = __devexit_p(savage4_remove),
167};
168
169static int __init i2c_savage4_init(void)
170{
171 return pci_register_driver(&savage4_driver);
172}
173
174static void __exit i2c_savage4_exit(void)
175{
176 pci_unregister_driver(&savage4_driver);
177}
178
179MODULE_AUTHOR("Alexander Wold <awold@bigfoot.com> "
180 "and Mark D. Studebaker <mdsxyz123@yahoo.com>");
181MODULE_DESCRIPTION("Savage4 I2C/SMBus driver");
182MODULE_LICENSE("GPL");
183
184module_init(i2c_savage4_init);
185module_exit(i2c_savage4_exit);