Another step toward making this thing a real multifunction device driver.
Cc: ScottFang@viatech.com.cn
Cc: JosephChan@via.com.tw
Cc: Harald Welte <laforge@gnumonks.org>
Acked-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Signed-off-by: Jonathan Corbet <corbet@lwn.net>
iounmap(vdev->engine_mmio);
}
+/*
+ * Create our subsidiary devices.
+ */
+static struct viafb_subdev_info {
+ char *name;
+ struct platform_device *platdev;
+} viafb_subdevs[] = {
+ {
+ .name = "viafb-gpio",
+ },
+ {
+ .name = "viafb-i2c",
+ }
+};
+#define N_SUBDEVS ARRAY_SIZE(viafb_subdevs)
+
+static int __devinit via_create_subdev(struct viafb_dev *vdev,
+ struct viafb_subdev_info *info)
+{
+ int ret;
+
+ info->platdev = platform_device_alloc(info->name, -1);
+ if (!info->platdev) {
+ dev_err(&vdev->pdev->dev, "Unable to allocate pdev %s\n",
+ info->name);
+ return -ENOMEM;
+ }
+ info->platdev->dev.parent = &vdev->pdev->dev;
+ info->platdev->dev.platform_data = vdev;
+ ret = platform_device_add(info->platdev);
+ if (ret) {
+ dev_err(&vdev->pdev->dev, "Unable to add pdev %s\n",
+ info->name);
+ platform_device_put(info->platdev);
+ info->platdev = NULL;
+ }
+ return ret;
+}
+
+static int __devinit via_setup_subdevs(struct viafb_dev *vdev)
+{
+ int i;
+
+ /*
+ * Ignore return values. Even if some of the devices
+ * fail to be created, we'll still be able to use some
+ * of the rest.
+ */
+ for (i = 0; i < N_SUBDEVS; i++)
+ via_create_subdev(vdev, viafb_subdevs + i);
+ return 0;
+}
+
+static void __devexit via_teardown_subdevs(void)
+{
+ int i;
+
+ for (i = 0; i < N_SUBDEVS; i++)
+ if (viafb_subdevs[i].platdev) {
+ viafb_subdevs[i].platdev->dev.platform_data = NULL;
+ platform_device_unregister(viafb_subdevs[i].platdev);
+ }
+}
+
static int __devinit via_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
memset(&global_dev, 0, sizeof(global_dev));
global_dev.pdev = pdev;
global_dev.chip_type = ent->driver_data;
+ global_dev.port_cfg = adap_configs;
spin_lock_init(&global_dev.reg_lock);
ret = via_pci_setup_mmio(&global_dev);
if (ret)
goto out_disable;
/*
- * Create the I2C busses. Bailing out on failure seems extreme,
- * but that's what the code did before.
+ * Create our subdevices. Continue even if some things fail.
*/
- ret = viafb_create_i2c_busses(&global_dev, adap_configs);
- if (ret)
- goto out_teardown;
+ via_setup_subdevs(&global_dev);
/*
* Set up the framebuffer.
*/
ret = via_fb_pci_probe(&global_dev);
if (ret)
- goto out_i2c;
- /*
- * Create the GPIOs. We continue whether or not this succeeds;
- * the framebuffer might be useful even without GPIO ports.
- */
- ret = viafb_create_gpios(&global_dev, adap_configs);
+ goto out_subdevs;
return 0;
-out_i2c:
- viafb_delete_i2c_busses();
-out_teardown:
+out_subdevs:
+ via_teardown_subdevs();
via_pci_teardown_mmio(&global_dev);
out_disable:
pci_disable_device(pdev);
static void __devexit via_pci_remove(struct pci_dev *pdev)
{
- viafb_destroy_gpios();
- viafb_delete_i2c_busses();
+ via_teardown_subdevs();
via_fb_pci_remove(pdev);
via_pci_teardown_mmio(&global_dev);
pci_disable_device(pdev);
ret = viafb_init();
if (ret)
return ret;
+ viafb_i2c_init();
+ viafb_gpio_init();
return pci_register_driver(&via_driver);
}
static void __exit via_core_exit(void)
{
pci_unregister_driver(&via_driver);
+ viafb_gpio_exit();
+ viafb_i2c_exit();
viafb_exit();
}
struct viafb_dev {
struct pci_dev *pdev;
int chip_type;
+ struct via_port_cfg *port_cfg;
/*
* Spinlock for access to device registers. Not yet
* globally used.
#include <linux/spinlock.h>
#include <linux/gpio.h>
+#include <linux/platform_device.h>
#include "via-core.h"
#include "via-gpio.h"
#include "global.h"
via_write_reg_mask(VIASR, gpio->vg_port_index, 0, 0x02);
}
+/*
+ * Look up a specific gpio and return the number it was assigned.
+ */
+int viafb_gpio_lookup(const char *name)
+{
+ int i;
+ for (i = 0; i < gpio_config.gpio_chip.ngpio; i++)
+ if (!strcmp(name, gpio_config.active_gpios[i]->vg_name))
+ return gpio_config.gpio_chip.base + i;
+ return -1;
+}
+EXPORT_SYMBOL_GPL(viafb_gpio_lookup);
-
-int viafb_create_gpios(struct viafb_dev *vdev,
- const struct via_port_cfg *port_cfg)
+/*
+ * Platform device stuff.
+ */
+static __devinit int viafb_gpio_probe(struct platform_device *platdev)
{
+ struct viafb_dev *vdev = platdev->dev.platform_data;
+ struct via_port_cfg *port_cfg = vdev->port_cfg;
int i, ngpio = 0, ret;
struct viafb_gpio *gpio;
unsigned long flags;
gpio_config.gpio_chip.ngpio = 0;
}
return ret;
-/* Port enable ? */
}
-int viafb_destroy_gpios(void)
+static int viafb_gpio_remove(struct platform_device *platdev)
{
unsigned long flags;
int ret = 0, i;
return ret;
}
-/*
- * Look up a specific gpio and return the number it was assigned.
- */
-int viafb_gpio_lookup(const char *name)
+static struct platform_driver via_gpio_driver = {
+ .driver = {
+ .name = "viafb-gpio",
+ },
+ .probe = viafb_gpio_probe,
+ .remove = viafb_gpio_remove,
+};
+
+int viafb_gpio_init(void)
{
- int i;
+ return platform_driver_register(&via_gpio_driver);
+}
- for (i = 0; i < gpio_config.gpio_chip.ngpio; i++)
- if (!strcmp(name, gpio_config.active_gpios[i]->vg_name))
- return gpio_config.gpio_chip.base + i;
- return -1;
+void viafb_gpio_exit(void)
+{
+ platform_driver_unregister(&via_gpio_driver);
}
-EXPORT_SYMBOL_GPL(viafb_gpio_lookup);
#ifndef __VIA_GPIO_H__
#define __VIA_GPIO_H__
-extern int viafb_create_gpios(struct viafb_dev *vdev,
- const struct via_port_cfg *port_cfg);
-extern int viafb_destroy_gpios(void);
extern int viafb_gpio_lookup(const char *name);
+extern int viafb_gpio_init(void);
+extern void viafb_gpio_exit(void);
#endif
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <linux/platform_device.h>
#include "via-core.h"
#include "via_i2c.h"
#include "global.h"
return i2c_bit_add_bus(adapter);
}
-int viafb_create_i2c_busses(struct viafb_dev *dev, struct via_port_cfg *configs)
+static int viafb_i2c_probe(struct platform_device *platdev)
{
int i, ret;
+ struct via_port_cfg *configs;
+
+ i2c_vdev = platdev->dev.platform_data;
+ configs = i2c_vdev->port_cfg;
- i2c_vdev = dev;
for (i = 0; i < VIAFB_NUM_PORTS; i++) {
struct via_port_cfg *adap_cfg = configs++;
struct via_i2c_stuff *i2c_stuff = &via_i2c_par[i];
return 0;
}
-void viafb_delete_i2c_busses(void)
+static int viafb_i2c_remove(struct platform_device *platdev)
{
int i;
if (i2c_stuff->adapter.algo_data == &i2c_stuff->algo)
i2c_del_adapter(&i2c_stuff->adapter);
}
+ return 0;
+}
+
+static struct platform_driver via_i2c_driver = {
+ .driver = {
+ .name = "viafb-i2c",
+ },
+ .probe = viafb_i2c_probe,
+ .remove = viafb_i2c_remove,
+};
+
+int viafb_i2c_init(void)
+{
+ return platform_driver_register(&via_i2c_driver);
+}
+
+void viafb_i2c_exit(void)
+{
+ platform_driver_unregister(&via_i2c_driver);
}
int viafb_i2c_writebyte(u8 adap, u8 slave_addr, u8 index, u8 data);
int viafb_i2c_readbytes(u8 adap, u8 slave_addr, u8 index, u8 *buff, int buff_len);
-struct viafb_par;
-int viafb_create_i2c_busses(struct viafb_dev *vdev, struct via_port_cfg *cfg);
-void viafb_delete_i2c_busses(void);
-struct i2c_adapter *viafb_find_adapter(enum viafb_i2c_adap which);
+extern int viafb_i2c_init(void);
+extern void viafb_i2c_exit(void);
#endif /* __VIA_I2C_H__ */