diff options
author | Raymond Tan <raymond.tan@intel.com> | 2015-02-01 21:52:51 -0500 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2015-03-12 05:07:17 -0400 |
commit | 60ae5b9f5cdd80c529eda13bfdd600a0fc857afb (patch) | |
tree | 5044e2b2e841ad0f70210687a868dfdb8ce82e8b /drivers/mfd | |
parent | e19f742885e87ab9582fdf5940f214419eb9275b (diff) |
mfd: intel_quark_i2c_gpio: Add Intel Quark X1000 I2C-GPIO MFD Driver
In Quark X1000, there's a single PCI device that provides both
an I2C controller and a GPIO controller. This MFD driver will
split the 2 devices for their respective drivers.
This patch is based on Josef Ahmad's initial work for Quark enabling.
Acked-by: Michael Turquette <mturquette@linaro.org>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Weike Chen <alvin.chen@intel.com>
Signed-off-by: Raymond Tan <raymond.tan@intel.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/Kconfig | 12 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/intel_quark_i2c_gpio.c | 277 |
3 files changed, 290 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f49f404c2004..b277f37ae3be 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
@@ -283,6 +283,18 @@ config HTC_I2CPLD | |||
283 | This device provides input and output GPIOs through an I2C | 283 | This device provides input and output GPIOs through an I2C |
284 | interface to one or more sub-chips. | 284 | interface to one or more sub-chips. |
285 | 285 | ||
286 | config MFD_INTEL_QUARK_I2C_GPIO | ||
287 | tristate "Intel Quark MFD I2C GPIO" | ||
288 | depends on PCI | ||
289 | depends on X86 | ||
290 | depends on COMMON_CLK | ||
291 | select MFD_CORE | ||
292 | help | ||
293 | This MFD provides support for I2C and GPIO that exist only | ||
294 | in a single PCI device. It splits the 2 IO devices to | ||
295 | their respective IO driver. | ||
296 | The GPIO exports a total amount of 8 interrupt-capable GPIOs. | ||
297 | |||
286 | config LPC_ICH | 298 | config LPC_ICH |
287 | tristate "Intel ICH LPC" | 299 | tristate "Intel ICH LPC" |
288 | depends on PCI | 300 | depends on PCI |
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 234998d77d43..5ebe443b595a 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile | |||
@@ -139,6 +139,7 @@ obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o | |||
139 | obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o | 139 | obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o |
140 | obj-$(CONFIG_PMIC_ADP5520) += adp5520.o | 140 | obj-$(CONFIG_PMIC_ADP5520) += adp5520.o |
141 | obj-$(CONFIG_MFD_KEMPLD) += kempld-core.o | 141 | obj-$(CONFIG_MFD_KEMPLD) += kempld-core.o |
142 | obj-$(CONFIG_MFD_INTEL_QUARK_I2C_GPIO) += intel_quark_i2c_gpio.o | ||
142 | obj-$(CONFIG_LPC_SCH) += lpc_sch.o | 143 | obj-$(CONFIG_LPC_SCH) += lpc_sch.o |
143 | obj-$(CONFIG_LPC_ICH) += lpc_ich.o | 144 | obj-$(CONFIG_LPC_ICH) += lpc_ich.o |
144 | obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o | 145 | obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o |
diff --git a/drivers/mfd/intel_quark_i2c_gpio.c b/drivers/mfd/intel_quark_i2c_gpio.c new file mode 100644 index 000000000000..006f2a1b1b1e --- /dev/null +++ b/drivers/mfd/intel_quark_i2c_gpio.c | |||
@@ -0,0 +1,277 @@ | |||
1 | /* | ||
2 | * Intel Quark MFD PCI driver for I2C & GPIO | ||
3 | * | ||
4 | * Copyright(c) 2014 Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | * Intel Quark PCI device for I2C and GPIO controller sharing the same | ||
16 | * PCI function. This PCI driver will split the 2 devices into their | ||
17 | * respective drivers. | ||
18 | */ | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/mfd/core.h> | ||
24 | #include <linux/clkdev.h> | ||
25 | #include <linux/clk-provider.h> | ||
26 | #include <linux/dmi.h> | ||
27 | #include <linux/platform_data/gpio-dwapb.h> | ||
28 | #include <linux/platform_data/i2c-designware.h> | ||
29 | |||
30 | /* PCI BAR for register base address */ | ||
31 | #define MFD_I2C_BAR 0 | ||
32 | #define MFD_GPIO_BAR 1 | ||
33 | |||
34 | /* The base GPIO number under GPIOLIB framework */ | ||
35 | #define INTEL_QUARK_MFD_GPIO_BASE 8 | ||
36 | |||
37 | /* The default number of South-Cluster GPIO on Quark. */ | ||
38 | #define INTEL_QUARK_MFD_NGPIO 8 | ||
39 | |||
40 | /* The DesignWare GPIO ports on Quark. */ | ||
41 | #define INTEL_QUARK_GPIO_NPORTS 1 | ||
42 | |||
43 | #define INTEL_QUARK_IORES_MEM 0 | ||
44 | #define INTEL_QUARK_IORES_IRQ 1 | ||
45 | |||
46 | #define INTEL_QUARK_I2C_CONTROLLER_CLK "i2c_designware.0" | ||
47 | |||
48 | /* The Quark I2C controller source clock */ | ||
49 | #define INTEL_QUARK_I2C_CLK_HZ 33000000 | ||
50 | |||
51 | #define INTEL_QUARK_I2C_NCLK 1 | ||
52 | |||
53 | struct intel_quark_mfd { | ||
54 | struct pci_dev *pdev; | ||
55 | struct clk *i2c_clk; | ||
56 | struct clk_lookup *i2c_clk_lookup; | ||
57 | }; | ||
58 | |||
59 | struct i2c_mode_info { | ||
60 | const char *name; | ||
61 | unsigned int i2c_scl_freq; | ||
62 | }; | ||
63 | |||
64 | static const struct i2c_mode_info platform_i2c_mode_info[] = { | ||
65 | { | ||
66 | .name = "Galileo", | ||
67 | .i2c_scl_freq = 100000, | ||
68 | }, | ||
69 | { | ||
70 | .name = "GalileoGen2", | ||
71 | .i2c_scl_freq = 400000, | ||
72 | }, | ||
73 | }; | ||
74 | |||
75 | static struct resource intel_quark_i2c_res[] = { | ||
76 | [INTEL_QUARK_IORES_MEM] = { | ||
77 | .flags = IORESOURCE_MEM, | ||
78 | }, | ||
79 | [INTEL_QUARK_IORES_IRQ] = { | ||
80 | .flags = IORESOURCE_IRQ, | ||
81 | }, | ||
82 | }; | ||
83 | |||
84 | static struct resource intel_quark_gpio_res[] = { | ||
85 | [INTEL_QUARK_IORES_MEM] = { | ||
86 | .flags = IORESOURCE_MEM, | ||
87 | }, | ||
88 | }; | ||
89 | |||
90 | static struct mfd_cell intel_quark_mfd_cells[] = { | ||
91 | { | ||
92 | .id = MFD_I2C_BAR, | ||
93 | .name = "i2c_designware", | ||
94 | .num_resources = ARRAY_SIZE(intel_quark_i2c_res), | ||
95 | .resources = intel_quark_i2c_res, | ||
96 | .ignore_resource_conflicts = true, | ||
97 | }, | ||
98 | { | ||
99 | .id = MFD_GPIO_BAR, | ||
100 | .name = "gpio-dwapb", | ||
101 | .num_resources = ARRAY_SIZE(intel_quark_gpio_res), | ||
102 | .resources = intel_quark_gpio_res, | ||
103 | .ignore_resource_conflicts = true, | ||
104 | }, | ||
105 | }; | ||
106 | |||
107 | static const struct pci_device_id intel_quark_mfd_ids[] = { | ||
108 | { PCI_VDEVICE(INTEL, 0x0934), }, | ||
109 | {}, | ||
110 | }; | ||
111 | MODULE_DEVICE_TABLE(pci, intel_quark_mfd_ids); | ||
112 | |||
113 | static int intel_quark_register_i2c_clk(struct intel_quark_mfd *quark_mfd) | ||
114 | { | ||
115 | struct pci_dev *pdev = quark_mfd->pdev; | ||
116 | struct clk_lookup *i2c_clk_lookup; | ||
117 | struct clk *i2c_clk; | ||
118 | int ret; | ||
119 | |||
120 | i2c_clk_lookup = devm_kcalloc(&pdev->dev, INTEL_QUARK_I2C_NCLK, | ||
121 | sizeof(*i2c_clk_lookup), GFP_KERNEL); | ||
122 | if (!i2c_clk_lookup) | ||
123 | return -ENOMEM; | ||
124 | |||
125 | i2c_clk_lookup[0].dev_id = INTEL_QUARK_I2C_CONTROLLER_CLK; | ||
126 | |||
127 | i2c_clk = clk_register_fixed_rate(&pdev->dev, | ||
128 | INTEL_QUARK_I2C_CONTROLLER_CLK, NULL, | ||
129 | CLK_IS_ROOT, INTEL_QUARK_I2C_CLK_HZ); | ||
130 | |||
131 | quark_mfd->i2c_clk_lookup = i2c_clk_lookup; | ||
132 | quark_mfd->i2c_clk = i2c_clk; | ||
133 | |||
134 | ret = clk_register_clkdevs(i2c_clk, i2c_clk_lookup, | ||
135 | INTEL_QUARK_I2C_NCLK); | ||
136 | if (ret) | ||
137 | dev_err(&pdev->dev, "Fixed clk register failed: %d\n", ret); | ||
138 | |||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | static void intel_quark_unregister_i2c_clk(struct pci_dev *pdev) | ||
143 | { | ||
144 | struct intel_quark_mfd *quark_mfd = dev_get_drvdata(&pdev->dev); | ||
145 | |||
146 | if (!quark_mfd->i2c_clk || !quark_mfd->i2c_clk_lookup) | ||
147 | return; | ||
148 | |||
149 | clkdev_drop(quark_mfd->i2c_clk_lookup); | ||
150 | clk_unregister(quark_mfd->i2c_clk); | ||
151 | } | ||
152 | |||
153 | static int intel_quark_i2c_setup(struct pci_dev *pdev, struct mfd_cell *cell) | ||
154 | { | ||
155 | const char *board_name = dmi_get_system_info(DMI_BOARD_NAME); | ||
156 | struct dw_i2c_platform_data *pdata; | ||
157 | struct resource *res = (struct resource *)cell->resources; | ||
158 | struct device *dev = &pdev->dev; | ||
159 | unsigned int i; | ||
160 | |||
161 | res[INTEL_QUARK_IORES_MEM].start = | ||
162 | pci_resource_start(pdev, MFD_I2C_BAR); | ||
163 | res[INTEL_QUARK_IORES_MEM].end = | ||
164 | pci_resource_end(pdev, MFD_I2C_BAR); | ||
165 | |||
166 | res[INTEL_QUARK_IORES_IRQ].start = pdev->irq; | ||
167 | res[INTEL_QUARK_IORES_IRQ].end = pdev->irq; | ||
168 | |||
169 | pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); | ||
170 | if (!pdata) | ||
171 | return -ENOMEM; | ||
172 | |||
173 | /* Fast mode by default */ | ||
174 | pdata->i2c_scl_freq = 400000; | ||
175 | |||
176 | for (i = 0; i < ARRAY_SIZE(platform_i2c_mode_info); i++) | ||
177 | if (!strcmp(board_name, platform_i2c_mode_info[i].name)) | ||
178 | pdata->i2c_scl_freq | ||
179 | = platform_i2c_mode_info[i].i2c_scl_freq; | ||
180 | |||
181 | cell->platform_data = pdata; | ||
182 | cell->pdata_size = sizeof(*pdata); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int intel_quark_gpio_setup(struct pci_dev *pdev, struct mfd_cell *cell) | ||
188 | { | ||
189 | struct dwapb_platform_data *pdata; | ||
190 | struct resource *res = (struct resource *)cell->resources; | ||
191 | struct device *dev = &pdev->dev; | ||
192 | |||
193 | res[INTEL_QUARK_IORES_MEM].start = | ||
194 | pci_resource_start(pdev, MFD_GPIO_BAR); | ||
195 | res[INTEL_QUARK_IORES_MEM].end = | ||
196 | pci_resource_end(pdev, MFD_GPIO_BAR); | ||
197 | |||
198 | pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); | ||
199 | if (!pdata) | ||
200 | return -ENOMEM; | ||
201 | |||
202 | /* For intel quark x1000, it has only one port: portA */ | ||
203 | pdata->nports = INTEL_QUARK_GPIO_NPORTS; | ||
204 | pdata->properties = devm_kcalloc(dev, pdata->nports, | ||
205 | sizeof(*pdata->properties), | ||
206 | GFP_KERNEL); | ||
207 | if (!pdata->properties) | ||
208 | return -ENOMEM; | ||
209 | |||
210 | /* Set the properties for portA */ | ||
211 | pdata->properties->node = NULL; | ||
212 | pdata->properties->name = "intel-quark-x1000-gpio-portA"; | ||
213 | pdata->properties->idx = 0; | ||
214 | pdata->properties->ngpio = INTEL_QUARK_MFD_NGPIO; | ||
215 | pdata->properties->gpio_base = INTEL_QUARK_MFD_GPIO_BASE; | ||
216 | pdata->properties->irq = pdev->irq; | ||
217 | pdata->properties->irq_shared = true; | ||
218 | |||
219 | cell->platform_data = pdata; | ||
220 | cell->pdata_size = sizeof(*pdata); | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int intel_quark_mfd_probe(struct pci_dev *pdev, | ||
226 | const struct pci_device_id *id) | ||
227 | { | ||
228 | struct intel_quark_mfd *quark_mfd; | ||
229 | int ret; | ||
230 | |||
231 | ret = pcim_enable_device(pdev); | ||
232 | if (ret) | ||
233 | return ret; | ||
234 | |||
235 | quark_mfd = devm_kzalloc(&pdev->dev, sizeof(*quark_mfd), GFP_KERNEL); | ||
236 | if (!quark_mfd) | ||
237 | return -ENOMEM; | ||
238 | quark_mfd->pdev = pdev; | ||
239 | |||
240 | ret = intel_quark_register_i2c_clk(quark_mfd); | ||
241 | if (ret) | ||
242 | return ret; | ||
243 | |||
244 | dev_set_drvdata(&pdev->dev, quark_mfd); | ||
245 | |||
246 | ret = intel_quark_i2c_setup(pdev, &intel_quark_mfd_cells[MFD_I2C_BAR]); | ||
247 | if (ret) | ||
248 | return ret; | ||
249 | |||
250 | ret = intel_quark_gpio_setup(pdev, | ||
251 | &intel_quark_mfd_cells[MFD_GPIO_BAR]); | ||
252 | if (ret) | ||
253 | return ret; | ||
254 | |||
255 | return mfd_add_devices(&pdev->dev, 0, intel_quark_mfd_cells, | ||
256 | ARRAY_SIZE(intel_quark_mfd_cells), NULL, 0, | ||
257 | NULL); | ||
258 | } | ||
259 | |||
260 | static void intel_quark_mfd_remove(struct pci_dev *pdev) | ||
261 | { | ||
262 | intel_quark_unregister_i2c_clk(pdev); | ||
263 | mfd_remove_devices(&pdev->dev); | ||
264 | } | ||
265 | |||
266 | static struct pci_driver intel_quark_mfd_driver = { | ||
267 | .name = "intel_quark_mfd_i2c_gpio", | ||
268 | .id_table = intel_quark_mfd_ids, | ||
269 | .probe = intel_quark_mfd_probe, | ||
270 | .remove = intel_quark_mfd_remove, | ||
271 | }; | ||
272 | |||
273 | module_pci_driver(intel_quark_mfd_driver); | ||
274 | |||
275 | MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>"); | ||
276 | MODULE_DESCRIPTION("Intel Quark MFD PCI driver for I2C & GPIO"); | ||
277 | MODULE_LICENSE("GPL v2"); | ||