Blame view

drivers/mfd/intel_quark_i2c_gpio.c 6.95 KB
2025cf9e1   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
2
3
4
5
6
  /*
   * Intel Quark MFD PCI driver for I2C & GPIO
   *
   * Copyright(c) 2014 Intel Corporation.
   *
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
   * Intel Quark PCI device for I2C and GPIO controller sharing the same
   * PCI function. This PCI driver will split the 2 devices into their
   * respective drivers.
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/pci.h>
  #include <linux/mfd/core.h>
  #include <linux/clkdev.h>
  #include <linux/clk-provider.h>
  #include <linux/dmi.h>
  #include <linux/platform_data/gpio-dwapb.h>
  #include <linux/platform_data/i2c-designware.h>
  
  /* PCI BAR for register base address */
  #define MFD_I2C_BAR		0
  #define MFD_GPIO_BAR		1
918fe70cf   Andy Shevchenko   mfd: intel_quark_...
25
26
27
  /* ACPI _ADR value to match the child node */
  #define MFD_ACPI_MATCH_GPIO	0ULL
  #define MFD_ACPI_MATCH_I2C	1ULL
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  /* The base GPIO number under GPIOLIB framework */
  #define INTEL_QUARK_MFD_GPIO_BASE	8
  
  /* The default number of South-Cluster GPIO on Quark. */
  #define INTEL_QUARK_MFD_NGPIO		8
  
  /* The DesignWare GPIO ports on Quark. */
  #define INTEL_QUARK_GPIO_NPORTS	1
  
  #define INTEL_QUARK_IORES_MEM	0
  #define INTEL_QUARK_IORES_IRQ	1
  
  #define INTEL_QUARK_I2C_CONTROLLER_CLK "i2c_designware.0"
  
  /* The Quark I2C controller source clock */
  #define INTEL_QUARK_I2C_CLK_HZ	33000000
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
44
  struct intel_quark_mfd {
9caac8863   Andy Shevchenko   mfd: intel_quark_...
45
  	struct device		*dev;
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
46
47
48
  	struct clk		*i2c_clk;
  	struct clk_lookup	*i2c_clk_lookup;
  };
b518d4adb   Jan Kiszka   mfd: intel_quark_...
49
  static const struct dmi_system_id dmi_platform_info[] = {
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
50
  	{
b518d4adb   Jan Kiszka   mfd: intel_quark_...
51
52
53
54
  		.matches = {
  			DMI_EXACT_MATCH(DMI_BOARD_NAME, "Galileo"),
  		},
  		.driver_data = (void *)100000,
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
55
56
  	},
  	{
b518d4adb   Jan Kiszka   mfd: intel_quark_...
57
58
59
60
  		.matches = {
  			DMI_EXACT_MATCH(DMI_BOARD_NAME, "GalileoGen2"),
  		},
  		.driver_data = (void *)400000,
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
61
  	},
842086d2b   Jan Kiszka   mfd: intel_quark_...
62
63
64
  	{
  		.matches = {
  			DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"),
842086d2b   Jan Kiszka   mfd: intel_quark_...
65
66
67
  		},
  		.driver_data = (void *)400000,
  	},
bafc1face   Andy Shevchenko   mfd: intel_quark_...
68
  	{}
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
69
70
71
72
73
74
75
76
77
78
  };
  
  static struct resource intel_quark_i2c_res[] = {
  	[INTEL_QUARK_IORES_MEM] = {
  		.flags = IORESOURCE_MEM,
  	},
  	[INTEL_QUARK_IORES_IRQ] = {
  		.flags = IORESOURCE_IRQ,
  	},
  };
918fe70cf   Andy Shevchenko   mfd: intel_quark_...
79
80
81
  static struct mfd_cell_acpi_match intel_quark_acpi_match_i2c = {
  	.adr = MFD_ACPI_MATCH_I2C,
  };
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
82
83
84
85
86
  static struct resource intel_quark_gpio_res[] = {
  	[INTEL_QUARK_IORES_MEM] = {
  		.flags = IORESOURCE_MEM,
  	},
  };
918fe70cf   Andy Shevchenko   mfd: intel_quark_...
87
88
89
  static struct mfd_cell_acpi_match intel_quark_acpi_match_gpio = {
  	.adr = MFD_ACPI_MATCH_GPIO,
  };
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
90
91
  static struct mfd_cell intel_quark_mfd_cells[] = {
  	{
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
92
93
  		.id = MFD_GPIO_BAR,
  		.name = "gpio-dwapb",
918fe70cf   Andy Shevchenko   mfd: intel_quark_...
94
  		.acpi_match = &intel_quark_acpi_match_gpio,
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
95
96
97
98
  		.num_resources = ARRAY_SIZE(intel_quark_gpio_res),
  		.resources = intel_quark_gpio_res,
  		.ignore_resource_conflicts = true,
  	},
236fd469f   Andy Shevchenko   mfd: intel_quark_...
99
100
101
  	{
  		.id = MFD_I2C_BAR,
  		.name = "i2c_designware",
918fe70cf   Andy Shevchenko   mfd: intel_quark_...
102
  		.acpi_match = &intel_quark_acpi_match_i2c,
236fd469f   Andy Shevchenko   mfd: intel_quark_...
103
104
105
106
  		.num_resources = ARRAY_SIZE(intel_quark_i2c_res),
  		.resources = intel_quark_i2c_res,
  		.ignore_resource_conflicts = true,
  	},
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
107
108
109
110
111
112
113
  };
  
  static const struct pci_device_id intel_quark_mfd_ids[] = {
  	{ PCI_VDEVICE(INTEL, 0x0934), },
  	{},
  };
  MODULE_DEVICE_TABLE(pci, intel_quark_mfd_ids);
9caac8863   Andy Shevchenko   mfd: intel_quark_...
114
  static int intel_quark_register_i2c_clk(struct device *dev)
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
115
  {
9caac8863   Andy Shevchenko   mfd: intel_quark_...
116
  	struct intel_quark_mfd *quark_mfd = dev_get_drvdata(dev);
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
117
  	struct clk *i2c_clk;
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
118

9caac8863   Andy Shevchenko   mfd: intel_quark_...
119
  	i2c_clk = clk_register_fixed_rate(dev,
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
120
  					  INTEL_QUARK_I2C_CONTROLLER_CLK, NULL,
36a0c0884   Stephen Boyd   mfd: intel_quark_...
121
  					  0, INTEL_QUARK_I2C_CLK_HZ);
c4726abce   Stephen Boyd   mfd: intel_quark_...
122
123
  	if (IS_ERR(i2c_clk))
  		return PTR_ERR(i2c_clk);
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
124

60ae5b9f5   Raymond Tan   mfd: intel_quark_...
125
  	quark_mfd->i2c_clk = i2c_clk;
c4726abce   Stephen Boyd   mfd: intel_quark_...
126
127
  	quark_mfd->i2c_clk_lookup = clkdev_create(i2c_clk, NULL,
  						INTEL_QUARK_I2C_CONTROLLER_CLK);
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
128

c4726abce   Stephen Boyd   mfd: intel_quark_...
129
  	if (!quark_mfd->i2c_clk_lookup) {
7f0c5ae18   Andy Shevchenko   mfd: intel_quark_...
130
  		clk_unregister(quark_mfd->i2c_clk);
9caac8863   Andy Shevchenko   mfd: intel_quark_...
131
132
  		dev_err(dev, "Fixed clk register failed
  ");
c4726abce   Stephen Boyd   mfd: intel_quark_...
133
134
  		return -ENOMEM;
  	}
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
135

c4726abce   Stephen Boyd   mfd: intel_quark_...
136
  	return 0;
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
137
  }
9caac8863   Andy Shevchenko   mfd: intel_quark_...
138
  static void intel_quark_unregister_i2c_clk(struct device *dev)
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
139
  {
9caac8863   Andy Shevchenko   mfd: intel_quark_...
140
  	struct intel_quark_mfd *quark_mfd = dev_get_drvdata(dev);
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
141

7f0c5ae18   Andy Shevchenko   mfd: intel_quark_...
142
  	if (!quark_mfd->i2c_clk_lookup)
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
143
144
145
146
147
148
149
150
  		return;
  
  	clkdev_drop(quark_mfd->i2c_clk_lookup);
  	clk_unregister(quark_mfd->i2c_clk);
  }
  
  static int intel_quark_i2c_setup(struct pci_dev *pdev, struct mfd_cell *cell)
  {
b518d4adb   Jan Kiszka   mfd: intel_quark_...
151
  	const struct dmi_system_id *dmi_id;
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
152
153
154
  	struct dw_i2c_platform_data *pdata;
  	struct resource *res = (struct resource *)cell->resources;
  	struct device *dev = &pdev->dev;
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
155
156
157
158
159
160
161
162
163
164
165
166
  
  	res[INTEL_QUARK_IORES_MEM].start =
  		pci_resource_start(pdev, MFD_I2C_BAR);
  	res[INTEL_QUARK_IORES_MEM].end =
  		pci_resource_end(pdev, MFD_I2C_BAR);
  
  	res[INTEL_QUARK_IORES_IRQ].start = pdev->irq;
  	res[INTEL_QUARK_IORES_IRQ].end = pdev->irq;
  
  	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
  	if (!pdata)
  		return -ENOMEM;
bafc1face   Andy Shevchenko   mfd: intel_quark_...
167
168
  	/* Normal mode by default */
  	pdata->i2c_scl_freq = 100000;
b518d4adb   Jan Kiszka   mfd: intel_quark_...
169
170
171
  	dmi_id = dmi_first_match(dmi_platform_info);
  	if (dmi_id)
  		pdata->i2c_scl_freq = (uintptr_t)dmi_id->driver_data;
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
  
  	cell->platform_data = pdata;
  	cell->pdata_size = sizeof(*pdata);
  
  	return 0;
  }
  
  static int intel_quark_gpio_setup(struct pci_dev *pdev, struct mfd_cell *cell)
  {
  	struct dwapb_platform_data *pdata;
  	struct resource *res = (struct resource *)cell->resources;
  	struct device *dev = &pdev->dev;
  
  	res[INTEL_QUARK_IORES_MEM].start =
  		pci_resource_start(pdev, MFD_GPIO_BAR);
  	res[INTEL_QUARK_IORES_MEM].end =
  		pci_resource_end(pdev, MFD_GPIO_BAR);
  
  	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
  	if (!pdata)
  		return -ENOMEM;
  
  	/* For intel quark x1000, it has only one port: portA */
  	pdata->nports = INTEL_QUARK_GPIO_NPORTS;
  	pdata->properties = devm_kcalloc(dev, pdata->nports,
  					 sizeof(*pdata->properties),
  					 GFP_KERNEL);
  	if (!pdata->properties)
  		return -ENOMEM;
  
  	/* Set the properties for portA */
4ba8cfa79   Jiang Qiu   gpio: dwapb: conv...
203
  	pdata->properties->fwnode	= NULL;
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
204
205
206
  	pdata->properties->idx		= 0;
  	pdata->properties->ngpio	= INTEL_QUARK_MFD_NGPIO;
  	pdata->properties->gpio_base	= INTEL_QUARK_MFD_GPIO_BASE;
e6ca26abd   Phil Edworthy   gpio: dwapb: Add ...
207
  	pdata->properties->irq[0]	= pdev->irq;
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
  	pdata->properties->irq_shared	= true;
  
  	cell->platform_data = pdata;
  	cell->pdata_size = sizeof(*pdata);
  
  	return 0;
  }
  
  static int intel_quark_mfd_probe(struct pci_dev *pdev,
  				 const struct pci_device_id *id)
  {
  	struct intel_quark_mfd *quark_mfd;
  	int ret;
  
  	ret = pcim_enable_device(pdev);
  	if (ret)
  		return ret;
  
  	quark_mfd = devm_kzalloc(&pdev->dev, sizeof(*quark_mfd), GFP_KERNEL);
  	if (!quark_mfd)
  		return -ENOMEM;
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
229

9caac8863   Andy Shevchenko   mfd: intel_quark_...
230
  	quark_mfd->dev = &pdev->dev;
7f0c5ae18   Andy Shevchenko   mfd: intel_quark_...
231
  	dev_set_drvdata(&pdev->dev, quark_mfd);
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
232

9caac8863   Andy Shevchenko   mfd: intel_quark_...
233
  	ret = intel_quark_register_i2c_clk(&pdev->dev);
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
234
235
  	if (ret)
  		return ret;
236fd469f   Andy Shevchenko   mfd: intel_quark_...
236
  	ret = intel_quark_i2c_setup(pdev, &intel_quark_mfd_cells[1]);
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
237
  	if (ret)
7f0c5ae18   Andy Shevchenko   mfd: intel_quark_...
238
  		goto err_unregister_i2c_clk;
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
239

236fd469f   Andy Shevchenko   mfd: intel_quark_...
240
  	ret = intel_quark_gpio_setup(pdev, &intel_quark_mfd_cells[0]);
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
241
  	if (ret)
7f0c5ae18   Andy Shevchenko   mfd: intel_quark_...
242
  		goto err_unregister_i2c_clk;
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
243

7f0c5ae18   Andy Shevchenko   mfd: intel_quark_...
244
245
246
247
248
249
250
  	ret = mfd_add_devices(&pdev->dev, 0, intel_quark_mfd_cells,
  			      ARRAY_SIZE(intel_quark_mfd_cells), NULL, 0,
  			      NULL);
  	if (ret)
  		goto err_unregister_i2c_clk;
  
  	return 0;
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
251

7f0c5ae18   Andy Shevchenko   mfd: intel_quark_...
252
  err_unregister_i2c_clk:
9caac8863   Andy Shevchenko   mfd: intel_quark_...
253
  	intel_quark_unregister_i2c_clk(&pdev->dev);
7f0c5ae18   Andy Shevchenko   mfd: intel_quark_...
254
  	return ret;
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
255
256
257
258
  }
  
  static void intel_quark_mfd_remove(struct pci_dev *pdev)
  {
9caac8863   Andy Shevchenko   mfd: intel_quark_...
259
  	intel_quark_unregister_i2c_clk(&pdev->dev);
60ae5b9f5   Raymond Tan   mfd: intel_quark_...
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
  	mfd_remove_devices(&pdev->dev);
  }
  
  static struct pci_driver intel_quark_mfd_driver = {
  	.name		= "intel_quark_mfd_i2c_gpio",
  	.id_table	= intel_quark_mfd_ids,
  	.probe		= intel_quark_mfd_probe,
  	.remove		= intel_quark_mfd_remove,
  };
  
  module_pci_driver(intel_quark_mfd_driver);
  
  MODULE_AUTHOR("Raymond Tan <raymond.tan@intel.com>");
  MODULE_DESCRIPTION("Intel Quark MFD PCI driver for I2C & GPIO");
  MODULE_LICENSE("GPL v2");