1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Industry-pack bus support functions.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2011-2012 CERN (www.cern.ch)
6*4882a593Smuzhiyun * Author: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/slab.h>
11*4882a593Smuzhiyun #include <linux/idr.h>
12*4882a593Smuzhiyun #include <linux/io.h>
13*4882a593Smuzhiyun #include <linux/ipack.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #define to_ipack_dev(device) container_of(device, struct ipack_device, dev)
16*4882a593Smuzhiyun #define to_ipack_driver(drv) container_of(drv, struct ipack_driver, driver)
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun static DEFINE_IDA(ipack_ida);
19*4882a593Smuzhiyun
ipack_device_release(struct device * dev)20*4882a593Smuzhiyun static void ipack_device_release(struct device *dev)
21*4882a593Smuzhiyun {
22*4882a593Smuzhiyun struct ipack_device *device = to_ipack_dev(dev);
23*4882a593Smuzhiyun kfree(device->id);
24*4882a593Smuzhiyun device->release(device);
25*4882a593Smuzhiyun }
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun static inline const struct ipack_device_id *
ipack_match_one_device(const struct ipack_device_id * id,const struct ipack_device * device)28*4882a593Smuzhiyun ipack_match_one_device(const struct ipack_device_id *id,
29*4882a593Smuzhiyun const struct ipack_device *device)
30*4882a593Smuzhiyun {
31*4882a593Smuzhiyun if ((id->format == IPACK_ANY_FORMAT ||
32*4882a593Smuzhiyun id->format == device->id_format) &&
33*4882a593Smuzhiyun (id->vendor == IPACK_ANY_ID || id->vendor == device->id_vendor) &&
34*4882a593Smuzhiyun (id->device == IPACK_ANY_ID || id->device == device->id_device))
35*4882a593Smuzhiyun return id;
36*4882a593Smuzhiyun return NULL;
37*4882a593Smuzhiyun }
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun static const struct ipack_device_id *
ipack_match_id(const struct ipack_device_id * ids,struct ipack_device * idev)40*4882a593Smuzhiyun ipack_match_id(const struct ipack_device_id *ids, struct ipack_device *idev)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun if (ids) {
43*4882a593Smuzhiyun while (ids->vendor || ids->device) {
44*4882a593Smuzhiyun if (ipack_match_one_device(ids, idev))
45*4882a593Smuzhiyun return ids;
46*4882a593Smuzhiyun ids++;
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun return NULL;
50*4882a593Smuzhiyun }
51*4882a593Smuzhiyun
ipack_bus_match(struct device * dev,struct device_driver * drv)52*4882a593Smuzhiyun static int ipack_bus_match(struct device *dev, struct device_driver *drv)
53*4882a593Smuzhiyun {
54*4882a593Smuzhiyun struct ipack_device *idev = to_ipack_dev(dev);
55*4882a593Smuzhiyun struct ipack_driver *idrv = to_ipack_driver(drv);
56*4882a593Smuzhiyun const struct ipack_device_id *found_id;
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun found_id = ipack_match_id(idrv->id_table, idev);
59*4882a593Smuzhiyun return found_id ? 1 : 0;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun
ipack_bus_probe(struct device * device)62*4882a593Smuzhiyun static int ipack_bus_probe(struct device *device)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun struct ipack_device *dev = to_ipack_dev(device);
65*4882a593Smuzhiyun struct ipack_driver *drv = to_ipack_driver(device->driver);
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun if (!drv->ops->probe)
68*4882a593Smuzhiyun return -EINVAL;
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun return drv->ops->probe(dev);
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun
ipack_bus_remove(struct device * device)73*4882a593Smuzhiyun static int ipack_bus_remove(struct device *device)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun struct ipack_device *dev = to_ipack_dev(device);
76*4882a593Smuzhiyun struct ipack_driver *drv = to_ipack_driver(device->driver);
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun if (!drv->ops->remove)
79*4882a593Smuzhiyun return -EINVAL;
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun drv->ops->remove(dev);
82*4882a593Smuzhiyun return 0;
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun
ipack_uevent(struct device * dev,struct kobj_uevent_env * env)85*4882a593Smuzhiyun static int ipack_uevent(struct device *dev, struct kobj_uevent_env *env)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun struct ipack_device *idev;
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun if (!dev)
90*4882a593Smuzhiyun return -ENODEV;
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun idev = to_ipack_dev(dev);
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun if (add_uevent_var(env,
95*4882a593Smuzhiyun "MODALIAS=ipack:f%02Xv%08Xd%08X", idev->id_format,
96*4882a593Smuzhiyun idev->id_vendor, idev->id_device))
97*4882a593Smuzhiyun return -ENOMEM;
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun return 0;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun #define ipack_device_attr(field, format_string) \
103*4882a593Smuzhiyun static ssize_t \
104*4882a593Smuzhiyun field##_show(struct device *dev, struct device_attribute *attr, \
105*4882a593Smuzhiyun char *buf) \
106*4882a593Smuzhiyun { \
107*4882a593Smuzhiyun struct ipack_device *idev = to_ipack_dev(dev); \
108*4882a593Smuzhiyun return sprintf(buf, format_string, idev->field); \
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun
id_show(struct device * dev,struct device_attribute * attr,char * buf)111*4882a593Smuzhiyun static ssize_t id_show(struct device *dev,
112*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun unsigned int i, c, l, s;
115*4882a593Smuzhiyun struct ipack_device *idev = to_ipack_dev(dev);
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun switch (idev->id_format) {
119*4882a593Smuzhiyun case IPACK_ID_VERSION_1:
120*4882a593Smuzhiyun l = 0x7; s = 1; break;
121*4882a593Smuzhiyun case IPACK_ID_VERSION_2:
122*4882a593Smuzhiyun l = 0xf; s = 2; break;
123*4882a593Smuzhiyun default:
124*4882a593Smuzhiyun return -EIO;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun c = 0;
127*4882a593Smuzhiyun for (i = 0; i < idev->id_avail; i++) {
128*4882a593Smuzhiyun if (i > 0) {
129*4882a593Smuzhiyun if ((i & l) == 0)
130*4882a593Smuzhiyun buf[c++] = '\n';
131*4882a593Smuzhiyun else if ((i & s) == 0)
132*4882a593Smuzhiyun buf[c++] = ' ';
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun sprintf(&buf[c], "%02x", idev->id[i]);
135*4882a593Smuzhiyun c += 2;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun buf[c++] = '\n';
138*4882a593Smuzhiyun return c;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun static ssize_t
id_vendor_show(struct device * dev,struct device_attribute * attr,char * buf)142*4882a593Smuzhiyun id_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun struct ipack_device *idev = to_ipack_dev(dev);
145*4882a593Smuzhiyun switch (idev->id_format) {
146*4882a593Smuzhiyun case IPACK_ID_VERSION_1:
147*4882a593Smuzhiyun return sprintf(buf, "0x%02x\n", idev->id_vendor);
148*4882a593Smuzhiyun case IPACK_ID_VERSION_2:
149*4882a593Smuzhiyun return sprintf(buf, "0x%06x\n", idev->id_vendor);
150*4882a593Smuzhiyun default:
151*4882a593Smuzhiyun return -EIO;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun static ssize_t
id_device_show(struct device * dev,struct device_attribute * attr,char * buf)156*4882a593Smuzhiyun id_device_show(struct device *dev, struct device_attribute *attr, char *buf)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun struct ipack_device *idev = to_ipack_dev(dev);
159*4882a593Smuzhiyun switch (idev->id_format) {
160*4882a593Smuzhiyun case IPACK_ID_VERSION_1:
161*4882a593Smuzhiyun return sprintf(buf, "0x%02x\n", idev->id_device);
162*4882a593Smuzhiyun case IPACK_ID_VERSION_2:
163*4882a593Smuzhiyun return sprintf(buf, "0x%04x\n", idev->id_device);
164*4882a593Smuzhiyun default:
165*4882a593Smuzhiyun return -EIO;
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
modalias_show(struct device * dev,struct device_attribute * attr,char * buf)169*4882a593Smuzhiyun static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
170*4882a593Smuzhiyun char *buf)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun struct ipack_device *idev = to_ipack_dev(dev);
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun return sprintf(buf, "ipac:f%02Xv%08Xd%08X", idev->id_format,
175*4882a593Smuzhiyun idev->id_vendor, idev->id_device);
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun ipack_device_attr(id_format, "0x%hhx\n");
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun static DEVICE_ATTR_RO(id);
181*4882a593Smuzhiyun static DEVICE_ATTR_RO(id_device);
182*4882a593Smuzhiyun static DEVICE_ATTR_RO(id_format);
183*4882a593Smuzhiyun static DEVICE_ATTR_RO(id_vendor);
184*4882a593Smuzhiyun static DEVICE_ATTR_RO(modalias);
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun static struct attribute *ipack_attrs[] = {
187*4882a593Smuzhiyun &dev_attr_id.attr,
188*4882a593Smuzhiyun &dev_attr_id_device.attr,
189*4882a593Smuzhiyun &dev_attr_id_format.attr,
190*4882a593Smuzhiyun &dev_attr_id_vendor.attr,
191*4882a593Smuzhiyun &dev_attr_modalias.attr,
192*4882a593Smuzhiyun NULL,
193*4882a593Smuzhiyun };
194*4882a593Smuzhiyun ATTRIBUTE_GROUPS(ipack);
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun static struct bus_type ipack_bus_type = {
197*4882a593Smuzhiyun .name = "ipack",
198*4882a593Smuzhiyun .probe = ipack_bus_probe,
199*4882a593Smuzhiyun .match = ipack_bus_match,
200*4882a593Smuzhiyun .remove = ipack_bus_remove,
201*4882a593Smuzhiyun .dev_groups = ipack_groups,
202*4882a593Smuzhiyun .uevent = ipack_uevent,
203*4882a593Smuzhiyun };
204*4882a593Smuzhiyun
ipack_bus_register(struct device * parent,int slots,const struct ipack_bus_ops * ops,struct module * owner)205*4882a593Smuzhiyun struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots,
206*4882a593Smuzhiyun const struct ipack_bus_ops *ops,
207*4882a593Smuzhiyun struct module *owner)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun int bus_nr;
210*4882a593Smuzhiyun struct ipack_bus_device *bus;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun bus = kzalloc(sizeof(*bus), GFP_KERNEL);
213*4882a593Smuzhiyun if (!bus)
214*4882a593Smuzhiyun return NULL;
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun bus_nr = ida_simple_get(&ipack_ida, 0, 0, GFP_KERNEL);
217*4882a593Smuzhiyun if (bus_nr < 0) {
218*4882a593Smuzhiyun kfree(bus);
219*4882a593Smuzhiyun return NULL;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun bus->bus_nr = bus_nr;
223*4882a593Smuzhiyun bus->parent = parent;
224*4882a593Smuzhiyun bus->slots = slots;
225*4882a593Smuzhiyun bus->ops = ops;
226*4882a593Smuzhiyun bus->owner = owner;
227*4882a593Smuzhiyun return bus;
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipack_bus_register);
230*4882a593Smuzhiyun
ipack_unregister_bus_member(struct device * dev,void * data)231*4882a593Smuzhiyun static int ipack_unregister_bus_member(struct device *dev, void *data)
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun struct ipack_device *idev = to_ipack_dev(dev);
234*4882a593Smuzhiyun struct ipack_bus_device *bus = data;
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun if (idev->bus == bus)
237*4882a593Smuzhiyun ipack_device_del(idev);
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun return 1;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun
ipack_bus_unregister(struct ipack_bus_device * bus)242*4882a593Smuzhiyun int ipack_bus_unregister(struct ipack_bus_device *bus)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun bus_for_each_dev(&ipack_bus_type, NULL, bus,
245*4882a593Smuzhiyun ipack_unregister_bus_member);
246*4882a593Smuzhiyun ida_simple_remove(&ipack_ida, bus->bus_nr);
247*4882a593Smuzhiyun kfree(bus);
248*4882a593Smuzhiyun return 0;
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipack_bus_unregister);
251*4882a593Smuzhiyun
ipack_driver_register(struct ipack_driver * edrv,struct module * owner,const char * name)252*4882a593Smuzhiyun int ipack_driver_register(struct ipack_driver *edrv, struct module *owner,
253*4882a593Smuzhiyun const char *name)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun edrv->driver.owner = owner;
256*4882a593Smuzhiyun edrv->driver.name = name;
257*4882a593Smuzhiyun edrv->driver.bus = &ipack_bus_type;
258*4882a593Smuzhiyun return driver_register(&edrv->driver);
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipack_driver_register);
261*4882a593Smuzhiyun
ipack_driver_unregister(struct ipack_driver * edrv)262*4882a593Smuzhiyun void ipack_driver_unregister(struct ipack_driver *edrv)
263*4882a593Smuzhiyun {
264*4882a593Smuzhiyun driver_unregister(&edrv->driver);
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipack_driver_unregister);
267*4882a593Smuzhiyun
ipack_crc_byte(u16 crc,u8 c)268*4882a593Smuzhiyun static u16 ipack_crc_byte(u16 crc, u8 c)
269*4882a593Smuzhiyun {
270*4882a593Smuzhiyun int i;
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun crc ^= c << 8;
273*4882a593Smuzhiyun for (i = 0; i < 8; i++)
274*4882a593Smuzhiyun crc = (crc << 1) ^ ((crc & 0x8000) ? 0x1021 : 0);
275*4882a593Smuzhiyun return crc;
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun /*
279*4882a593Smuzhiyun * The algorithm in lib/crc-ccitt.c does not seem to apply since it uses the
280*4882a593Smuzhiyun * opposite bit ordering.
281*4882a593Smuzhiyun */
ipack_calc_crc1(struct ipack_device * dev)282*4882a593Smuzhiyun static u8 ipack_calc_crc1(struct ipack_device *dev)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun u8 c;
285*4882a593Smuzhiyun u16 crc;
286*4882a593Smuzhiyun unsigned int i;
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun crc = 0xffff;
289*4882a593Smuzhiyun for (i = 0; i < dev->id_avail; i++) {
290*4882a593Smuzhiyun c = (i != 11) ? dev->id[i] : 0;
291*4882a593Smuzhiyun crc = ipack_crc_byte(crc, c);
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun crc = ~crc;
294*4882a593Smuzhiyun return crc & 0xff;
295*4882a593Smuzhiyun }
296*4882a593Smuzhiyun
ipack_calc_crc2(struct ipack_device * dev)297*4882a593Smuzhiyun static u16 ipack_calc_crc2(struct ipack_device *dev)
298*4882a593Smuzhiyun {
299*4882a593Smuzhiyun u8 c;
300*4882a593Smuzhiyun u16 crc;
301*4882a593Smuzhiyun unsigned int i;
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun crc = 0xffff;
304*4882a593Smuzhiyun for (i = 0; i < dev->id_avail; i++) {
305*4882a593Smuzhiyun c = ((i != 0x18) && (i != 0x19)) ? dev->id[i] : 0;
306*4882a593Smuzhiyun crc = ipack_crc_byte(crc, c);
307*4882a593Smuzhiyun }
308*4882a593Smuzhiyun crc = ~crc;
309*4882a593Smuzhiyun return crc;
310*4882a593Smuzhiyun }
311*4882a593Smuzhiyun
ipack_parse_id1(struct ipack_device * dev)312*4882a593Smuzhiyun static void ipack_parse_id1(struct ipack_device *dev)
313*4882a593Smuzhiyun {
314*4882a593Smuzhiyun u8 *id = dev->id;
315*4882a593Smuzhiyun u8 crc;
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun dev->id_vendor = id[4];
318*4882a593Smuzhiyun dev->id_device = id[5];
319*4882a593Smuzhiyun dev->speed_8mhz = 1;
320*4882a593Smuzhiyun dev->speed_32mhz = (id[7] == 'H');
321*4882a593Smuzhiyun crc = ipack_calc_crc1(dev);
322*4882a593Smuzhiyun dev->id_crc_correct = (crc == id[11]);
323*4882a593Smuzhiyun if (!dev->id_crc_correct) {
324*4882a593Smuzhiyun dev_warn(&dev->dev, "ID CRC invalid found 0x%x, expected 0x%x.\n",
325*4882a593Smuzhiyun id[11], crc);
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun
ipack_parse_id2(struct ipack_device * dev)329*4882a593Smuzhiyun static void ipack_parse_id2(struct ipack_device *dev)
330*4882a593Smuzhiyun {
331*4882a593Smuzhiyun __be16 *id = (__be16 *) dev->id;
332*4882a593Smuzhiyun u16 flags, crc;
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun dev->id_vendor = ((be16_to_cpu(id[3]) & 0xff) << 16)
335*4882a593Smuzhiyun + be16_to_cpu(id[4]);
336*4882a593Smuzhiyun dev->id_device = be16_to_cpu(id[5]);
337*4882a593Smuzhiyun flags = be16_to_cpu(id[10]);
338*4882a593Smuzhiyun dev->speed_8mhz = !!(flags & 2);
339*4882a593Smuzhiyun dev->speed_32mhz = !!(flags & 4);
340*4882a593Smuzhiyun crc = ipack_calc_crc2(dev);
341*4882a593Smuzhiyun dev->id_crc_correct = (crc == be16_to_cpu(id[12]));
342*4882a593Smuzhiyun if (!dev->id_crc_correct) {
343*4882a593Smuzhiyun dev_warn(&dev->dev, "ID CRC invalid found 0x%x, expected 0x%x.\n",
344*4882a593Smuzhiyun id[11], crc);
345*4882a593Smuzhiyun }
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun
ipack_device_read_id(struct ipack_device * dev)348*4882a593Smuzhiyun static int ipack_device_read_id(struct ipack_device *dev)
349*4882a593Smuzhiyun {
350*4882a593Smuzhiyun u8 __iomem *idmem;
351*4882a593Smuzhiyun int i;
352*4882a593Smuzhiyun int ret = 0;
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun idmem = ioremap(dev->region[IPACK_ID_SPACE].start,
355*4882a593Smuzhiyun dev->region[IPACK_ID_SPACE].size);
356*4882a593Smuzhiyun if (!idmem) {
357*4882a593Smuzhiyun dev_err(&dev->dev, "error mapping memory\n");
358*4882a593Smuzhiyun return -ENOMEM;
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun /* Determine ID PROM Data Format. If we find the ids "IPAC" or "IPAH"
362*4882a593Smuzhiyun * we are dealing with a IndustryPack format 1 device. If we detect
363*4882a593Smuzhiyun * "VITA4 " (16 bit big endian formatted) we are dealing with a
364*4882a593Smuzhiyun * IndustryPack format 2 device */
365*4882a593Smuzhiyun if ((ioread8(idmem + 1) == 'I') &&
366*4882a593Smuzhiyun (ioread8(idmem + 3) == 'P') &&
367*4882a593Smuzhiyun (ioread8(idmem + 5) == 'A') &&
368*4882a593Smuzhiyun ((ioread8(idmem + 7) == 'C') ||
369*4882a593Smuzhiyun (ioread8(idmem + 7) == 'H'))) {
370*4882a593Smuzhiyun dev->id_format = IPACK_ID_VERSION_1;
371*4882a593Smuzhiyun dev->id_avail = ioread8(idmem + 0x15);
372*4882a593Smuzhiyun if ((dev->id_avail < 0x0c) || (dev->id_avail > 0x40)) {
373*4882a593Smuzhiyun dev_warn(&dev->dev, "invalid id size");
374*4882a593Smuzhiyun dev->id_avail = 0x0c;
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun } else if ((ioread8(idmem + 0) == 'I') &&
377*4882a593Smuzhiyun (ioread8(idmem + 1) == 'V') &&
378*4882a593Smuzhiyun (ioread8(idmem + 2) == 'A') &&
379*4882a593Smuzhiyun (ioread8(idmem + 3) == 'T') &&
380*4882a593Smuzhiyun (ioread8(idmem + 4) == ' ') &&
381*4882a593Smuzhiyun (ioread8(idmem + 5) == '4')) {
382*4882a593Smuzhiyun dev->id_format = IPACK_ID_VERSION_2;
383*4882a593Smuzhiyun dev->id_avail = ioread16be(idmem + 0x16);
384*4882a593Smuzhiyun if ((dev->id_avail < 0x1a) || (dev->id_avail > 0x40)) {
385*4882a593Smuzhiyun dev_warn(&dev->dev, "invalid id size");
386*4882a593Smuzhiyun dev->id_avail = 0x1a;
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun } else {
389*4882a593Smuzhiyun dev->id_format = IPACK_ID_VERSION_INVALID;
390*4882a593Smuzhiyun dev->id_avail = 0;
391*4882a593Smuzhiyun }
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun if (!dev->id_avail) {
394*4882a593Smuzhiyun ret = -ENODEV;
395*4882a593Smuzhiyun goto out;
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun /* Obtain the amount of memory required to store a copy of the complete
399*4882a593Smuzhiyun * ID ROM contents */
400*4882a593Smuzhiyun dev->id = kmalloc(dev->id_avail, GFP_KERNEL);
401*4882a593Smuzhiyun if (!dev->id) {
402*4882a593Smuzhiyun ret = -ENOMEM;
403*4882a593Smuzhiyun goto out;
404*4882a593Smuzhiyun }
405*4882a593Smuzhiyun for (i = 0; i < dev->id_avail; i++) {
406*4882a593Smuzhiyun if (dev->id_format == IPACK_ID_VERSION_1)
407*4882a593Smuzhiyun dev->id[i] = ioread8(idmem + (i << 1) + 1);
408*4882a593Smuzhiyun else
409*4882a593Smuzhiyun dev->id[i] = ioread8(idmem + i);
410*4882a593Smuzhiyun }
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun /* now we can finally work with the copy */
413*4882a593Smuzhiyun switch (dev->id_format) {
414*4882a593Smuzhiyun case IPACK_ID_VERSION_1:
415*4882a593Smuzhiyun ipack_parse_id1(dev);
416*4882a593Smuzhiyun break;
417*4882a593Smuzhiyun case IPACK_ID_VERSION_2:
418*4882a593Smuzhiyun ipack_parse_id2(dev);
419*4882a593Smuzhiyun break;
420*4882a593Smuzhiyun }
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun out:
423*4882a593Smuzhiyun iounmap(idmem);
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun return ret;
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun
ipack_device_init(struct ipack_device * dev)428*4882a593Smuzhiyun int ipack_device_init(struct ipack_device *dev)
429*4882a593Smuzhiyun {
430*4882a593Smuzhiyun int ret;
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun dev->dev.bus = &ipack_bus_type;
433*4882a593Smuzhiyun dev->dev.release = ipack_device_release;
434*4882a593Smuzhiyun dev->dev.parent = dev->bus->parent;
435*4882a593Smuzhiyun dev_set_name(&dev->dev,
436*4882a593Smuzhiyun "ipack-dev.%u.%u", dev->bus->bus_nr, dev->slot);
437*4882a593Smuzhiyun device_initialize(&dev->dev);
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun if (dev->bus->ops->set_clockrate(dev, 8))
440*4882a593Smuzhiyun dev_warn(&dev->dev, "failed to switch to 8 MHz operation for reading of device ID.\n");
441*4882a593Smuzhiyun if (dev->bus->ops->reset_timeout(dev))
442*4882a593Smuzhiyun dev_warn(&dev->dev, "failed to reset potential timeout.");
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun ret = ipack_device_read_id(dev);
445*4882a593Smuzhiyun if (ret < 0) {
446*4882a593Smuzhiyun dev_err(&dev->dev, "error reading device id section.\n");
447*4882a593Smuzhiyun return ret;
448*4882a593Smuzhiyun }
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun /* if the device supports 32 MHz operation, use it. */
451*4882a593Smuzhiyun if (dev->speed_32mhz) {
452*4882a593Smuzhiyun ret = dev->bus->ops->set_clockrate(dev, 32);
453*4882a593Smuzhiyun if (ret < 0)
454*4882a593Smuzhiyun dev_err(&dev->dev, "failed to switch to 32 MHz operation.\n");
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun return 0;
458*4882a593Smuzhiyun }
459*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipack_device_init);
460*4882a593Smuzhiyun
ipack_device_add(struct ipack_device * dev)461*4882a593Smuzhiyun int ipack_device_add(struct ipack_device *dev)
462*4882a593Smuzhiyun {
463*4882a593Smuzhiyun return device_add(&dev->dev);
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipack_device_add);
466*4882a593Smuzhiyun
ipack_device_del(struct ipack_device * dev)467*4882a593Smuzhiyun void ipack_device_del(struct ipack_device *dev)
468*4882a593Smuzhiyun {
469*4882a593Smuzhiyun device_del(&dev->dev);
470*4882a593Smuzhiyun ipack_put_device(dev);
471*4882a593Smuzhiyun }
472*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipack_device_del);
473*4882a593Smuzhiyun
ipack_get_device(struct ipack_device * dev)474*4882a593Smuzhiyun void ipack_get_device(struct ipack_device *dev)
475*4882a593Smuzhiyun {
476*4882a593Smuzhiyun get_device(&dev->dev);
477*4882a593Smuzhiyun }
478*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipack_get_device);
479*4882a593Smuzhiyun
ipack_put_device(struct ipack_device * dev)480*4882a593Smuzhiyun void ipack_put_device(struct ipack_device *dev)
481*4882a593Smuzhiyun {
482*4882a593Smuzhiyun put_device(&dev->dev);
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(ipack_put_device);
485*4882a593Smuzhiyun
ipack_init(void)486*4882a593Smuzhiyun static int __init ipack_init(void)
487*4882a593Smuzhiyun {
488*4882a593Smuzhiyun ida_init(&ipack_ida);
489*4882a593Smuzhiyun return bus_register(&ipack_bus_type);
490*4882a593Smuzhiyun }
491*4882a593Smuzhiyun
ipack_exit(void)492*4882a593Smuzhiyun static void __exit ipack_exit(void)
493*4882a593Smuzhiyun {
494*4882a593Smuzhiyun bus_unregister(&ipack_bus_type);
495*4882a593Smuzhiyun ida_destroy(&ipack_ida);
496*4882a593Smuzhiyun }
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun module_init(ipack_init);
499*4882a593Smuzhiyun module_exit(ipack_exit);
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun MODULE_AUTHOR("Samuel Iglesias Gonsalvez <siglesias@igalia.com>");
502*4882a593Smuzhiyun MODULE_LICENSE("GPL");
503*4882a593Smuzhiyun MODULE_DESCRIPTION("Industry-pack bus core");
504