From 74a9d5f1d140adbeadd5ef319f162903090c3b83 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 3 Nov 2006 09:52:49 +0100 Subject: [PATCH] Remove inclusion of asm/processor.h in via82cxxx.c There is some PPC_CHRP specific code in drivers/ide/pci/via82cxxx.c, so #ifdef on CONFIG_PPC_CHRP instead of CONFIG_PPC_MULTIPLATFORM. Signed-off-by: Sascha Hauer Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- drivers/ide/pci/via82cxxx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c index 2af634d7acf..eb7ab112c05 100644 --- a/drivers/ide/pci/via82cxxx.c +++ b/drivers/ide/pci/via82cxxx.c @@ -35,7 +35,7 @@ #include #include -#ifdef CONFIG_PPC_MULTIPLATFORM +#ifdef CONFIG_PPC_CHRP #include #endif @@ -442,7 +442,7 @@ static void __devinit init_hwif_via82cxxx(ide_hwif_t *hwif) hwif->speedproc = &via_set_drive; -#if defined(CONFIG_PPC_CHRP) && defined(CONFIG_PPC32) +#ifdef CONFIG_PPC_CHRP if(machine_is(chrp) && _chrp_type == _CHRP_Pegasos) { hwif->irq = hwif->channel ? 15 : 14; } -- cgit v1.2.3 From bfaadcadc9549c7e296e37a0cfe306704e8df739 Mon Sep 17 00:00:00 2001 From: "s.hauer@pengutronix.de" Date: Thu, 2 Nov 2006 13:55:57 +0100 Subject: [PATCH] Replace CONFIG_PPC_MULTIPLATFORM with CONFIG_PPC_PMAC in tulip driver Replace CONFIG_PPC_MULTIPLATFORM with CONFIG_PPC_PMAC in drivers/net/tulip/de4x5.c. It is needed for a pmac specific hook but has nothing with to do with PPC_MULTIPLATFORM. Signed-off-by: Sascha Hauer Acked-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- drivers/net/tulip/de4x5.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c index 3f4b6408b75..4b3cd3d8b62 100644 --- a/drivers/net/tulip/de4x5.c +++ b/drivers/net/tulip/de4x5.c @@ -473,9 +473,9 @@ #include #include #include -#ifdef CONFIG_PPC_MULTIPLATFORM +#ifdef CONFIG_PPC_PMAC #include -#endif /* CONFIG_PPC_MULTIPLATFORM */ +#endif /* CONFIG_PPC_PMAC */ #include "de4x5.h" @@ -4151,7 +4151,7 @@ get_hw_addr(struct net_device *dev) /* If possible, try to fix a broken card - SMC only so far */ srom_repair(dev, broken); -#ifdef CONFIG_PPC_MULTIPLATFORM +#ifdef CONFIG_PPC_PMAC /* ** If the address starts with 00 a0, we have to bit-reverse ** each byte of the address. @@ -4168,7 +4168,7 @@ get_hw_addr(struct net_device *dev) dev->dev_addr[i] = ((x & 0x55) << 1) + ((x & 0xaa) >> 1); } } -#endif /* CONFIG_PPC_MULTIPLATFORM */ +#endif /* CONFIG_PPC_PMAC */ /* Test for a bad enet address */ status = test_bad_enet(dev, status); -- cgit v1.2.3 From a9b14973a8c42b2aecc968851372203c6567e196 Mon Sep 17 00:00:00 2001 From: Andy Fleming Date: Thu, 19 Oct 2006 19:52:26 -0500 Subject: [PATCH] Slight refactor of interrupt mapping for FSL parts * Cleaned up interrupt mapping a little by adding a helper function which parses the irq out of the device-tree, and puts it into a resource. * Changed the arch/ppc platform files to specify PHY_POLL, instead of -1 * Changed the fixed phy to use PHY_IGNORE_INTERRUPT * Added ethtool.h and mii.h to phy.h includes Signed-off-by: Paul Mackerras --- drivers/net/phy/fixed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c index f14e99276db..096d4a100bf 100644 --- a/drivers/net/phy/fixed.c +++ b/drivers/net/phy/fixed.c @@ -254,7 +254,7 @@ static int fixed_mdio_register_device(int number, int speed, int duplex) goto device_create_fail; } - phydev->irq = -1; + phydev->irq = PHY_IGNORE_INTERRUPT; phydev->dev.bus = &mdio_bus_type; if(number) -- cgit v1.2.3 From 1be3770aa9220324e54851d1be7c879942f79620 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sat, 11 Nov 2006 17:24:54 +1100 Subject: [POWERPC] Make EMAC use generic DCR access methods This patch makes the EMAC driver use the new DCR access methods. It doesn't yet uses dcr_map() and thus still only work with real DCRs. This will be fixed in a later patch Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- drivers/net/ibm_emac/ibm_emac_mal.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ibm_emac/ibm_emac_mal.h b/drivers/net/ibm_emac/ibm_emac_mal.h index f73f10a0a56..407d2acbf7c 100644 --- a/drivers/net/ibm_emac/ibm_emac_mal.h +++ b/drivers/net/ibm_emac/ibm_emac_mal.h @@ -24,6 +24,7 @@ #include #include +#include /* * These MAL "versions" probably aren't the real versions IBM uses for these @@ -191,6 +192,7 @@ struct mal_commac { struct ibm_ocp_mal { int dcrbase; + dcr_host_t dcrhost; struct list_head poll_list; struct net_device poll_dev; @@ -207,12 +209,12 @@ struct ibm_ocp_mal { static inline u32 get_mal_dcrn(struct ibm_ocp_mal *mal, int reg) { - return mfdcr(mal->dcrbase + reg); + return dcr_read(mal->dcrhost, mal->dcrbase + reg); } static inline void set_mal_dcrn(struct ibm_ocp_mal *mal, int reg, u32 val) { - mtdcr(mal->dcrbase + reg, val); + dcr_write(mal->dcrhost, mal->dcrbase + reg, val); } /* Register MAL devices */ -- cgit v1.2.3 From 7eebde700fe6fd6573e80bd8e5ed82b4ae705575 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sat, 11 Nov 2006 17:24:59 +1100 Subject: [POWERPC] Souped-up of_platform_device support This patch first splits of_device.c and of_platform.c, the later containing the bits relative to of_platform_device's. On the "breaks" side of things, drivers uisng of_platform_device(s) need to include asm/of_platform.h now and of_(un)register_driver is now of_(un)register_platform_driver. In addition to a few utility functions to locate of_platform_device(s), the main new addition is of_platform_bus_probe() which allows the platform code to trigger an automatic creation of of_platform_devices for a whole tree of devices. The function acts based on the type of the various "parent" devices encountered from a provided root, using either a default known list of bus types that can be "probed" or a passed-in list. It will only register devices on busses matching that list, which mean that typically, it will not register PCI devices, as expected (since they will be picked up by the PCI layer). This will be used by Cell platforms using 4xx-type IOs in the Axon bridge and can be used by any embedded-type device as well. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- drivers/macintosh/smu.c | 3 ++- drivers/macintosh/therm_adt746x.c | 2 +- drivers/macintosh/therm_pm72.c | 5 +++-- drivers/macintosh/therm_windtunnel.c | 7 ++++--- drivers/video/platinumfb.c | 5 +++-- 5 files changed, 13 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index ade25b3fbb3..4f724cdd2ef 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -46,6 +46,7 @@ #include #include #include +#include #define VERSION "0.7" #define AUTHOR "(c) 2005 Benjamin Herrenschmidt, IBM Corp." @@ -653,7 +654,7 @@ static int __init smu_init_sysfs(void) * I'm a bit too far from figuring out how that works with those * new chipsets, but that will come back and bite us */ - of_register_driver(&smu_of_platform_driver); + of_register_platform_driver(&smu_of_platform_driver); return 0; } diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index a0f30d0853e..13b953ae8eb 100644 --- a/drivers/macintosh/therm_adt746x.c +++ b/drivers/macintosh/therm_adt746x.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #undef DEBUG diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c index d00c0c37a12..2e4ad44a863 100644 --- a/drivers/macintosh/therm_pm72.c +++ b/drivers/macintosh/therm_pm72.c @@ -129,6 +129,7 @@ #include #include #include +#include #include "therm_pm72.h" @@ -2236,14 +2237,14 @@ static int __init therm_pm72_init(void) return -ENODEV; } - of_register_driver(&fcu_of_platform_driver); + of_register_platform_driver(&fcu_of_platform_driver); return 0; } static void __exit therm_pm72_exit(void) { - of_unregister_driver(&fcu_of_platform_driver); + of_unregister_platform_driver(&fcu_of_platform_driver); if (of_dev) of_device_unregister(of_dev); diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index 738faab1b22..a1d3a987cb3 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -36,12 +36,13 @@ #include #include #include + #include #include #include #include #include -#include +#include #include #define LOG_TEMP 0 /* continously log temperature */ @@ -511,14 +512,14 @@ g4fan_init( void ) return -ENODEV; } - of_register_driver( &therm_of_driver ); + of_register_platform_driver( &therm_of_driver ); return 0; } static void __exit g4fan_exit( void ) { - of_unregister_driver( &therm_of_driver ); + of_unregister_platform_driver( &therm_of_driver ); if( x.of_dev ) of_device_unregister( x.of_dev ); diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index fdb33cd21a2..cb26c6df058 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "macmodes.h" #include "platinumfb.h" @@ -682,14 +683,14 @@ static int __init platinumfb_init(void) return -ENODEV; platinumfb_setup(option); #endif - of_register_driver(&platinum_driver); + of_register_platform_driver(&platinum_driver); return 0; } static void __exit platinumfb_exit(void) { - of_unregister_driver(&platinum_driver); + of_unregister_platform_driver(&platinum_driver); } MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 7c719871ff4d5f15b71f0138d08b758281b58631 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sat, 11 Nov 2006 17:24:42 +1100 Subject: [PATCH] ibmveth: Remove ibmveth "liobn" field Remove the now unused "liobn" field in ibmveth which also avoids having insider knowledge of the iommu table in that driver. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Santiago Leon Signed-off-by: Paul Mackerras --- drivers/net/ibmveth.c | 4 ---- drivers/net/ibmveth.h | 1 - 2 files changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index 44c9f993dcc..99343b5836b 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -50,7 +50,6 @@ #include #include #include -#include #include #include #include @@ -1000,8 +999,6 @@ static int __devinit ibmveth_probe(struct vio_dev *dev, const struct vio_device_ adapter->mac_addr = 0; memcpy(&adapter->mac_addr, mac_addr_p, 6); - adapter->liobn = dev->iommu_table->it_index; - netdev->irq = dev->irq; netdev->open = ibmveth_open; netdev->poll = ibmveth_poll; @@ -1115,7 +1112,6 @@ static int ibmveth_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "%s %s\n\n", ibmveth_driver_string, ibmveth_driver_version); seq_printf(seq, "Unit Address: 0x%x\n", adapter->vdev->unit_address); - seq_printf(seq, "LIOBN: 0x%lx\n", adapter->liobn); seq_printf(seq, "Current MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", current_mac[0], current_mac[1], current_mac[2], current_mac[3], current_mac[4], current_mac[5]); diff --git a/drivers/net/ibmveth.h b/drivers/net/ibmveth.h index f5b25bff154..bb69ccae8ac 100644 --- a/drivers/net/ibmveth.h +++ b/drivers/net/ibmveth.h @@ -118,7 +118,6 @@ struct ibmveth_adapter { struct net_device_stats stats; unsigned int mcastFilterSize; unsigned long mac_addr; - unsigned long liobn; void * buffer_list_addr; void * filter_list_addr; dma_addr_t buffer_list_dma; -- cgit v1.2.3 From 3bc0f40c287a435805b0545ffc44ea41f11cd43e Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sat, 11 Nov 2006 17:25:13 +1100 Subject: [POWERPC] Spider uses low level BE MMIO accessors We use the powerpc specific low level MMIO accessor variants instead of readl() or readl_be() because we know spidernet is not a real PCI device and we can thus avoid the performance hit caused by the PCI workarounds. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- drivers/net/spider_net.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index cef7e6671c4..13e0a43e423 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -88,12 +88,11 @@ MODULE_DEVICE_TABLE(pci, spider_net_pci_tbl); static inline u32 spider_net_read_reg(struct spider_net_card *card, u32 reg) { - u32 value; - - value = readl(card->regs + reg); - value = le32_to_cpu(value); - - return value; + /* We use the powerpc specific variants instead of readl_be() because + * we know spidernet is not a real PCI device and we can thus avoid the + * performance hit caused by the PCI workarounds. + */ + return in_be32(card->regs + reg); } /** @@ -105,8 +104,11 @@ spider_net_read_reg(struct spider_net_card *card, u32 reg) static inline void spider_net_write_reg(struct spider_net_card *card, u32 reg, u32 value) { - value = cpu_to_le32(value); - writel(value, card->regs + reg); + /* We use the powerpc specific variants instead of writel_be() because + * we know spidernet is not a real PCI device and we can thus avoid the + * performance hit caused by the PCI workarounds. + */ + out_be32(card->regs + reg, value); } /** spider_net_write_phy - write to phy register -- cgit v1.2.3 From f9df68ec7bf49e37b46aa0a5d9e7938c060dba52 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 13 Nov 2006 14:43:17 +1100 Subject: [POWERPC] iSeries: improve viodasd initialisation On error, make sure that we undo all necessary operations. This also gets rid of a must_check warning. Signed-off-by: Stephen Rothwell Signed-off-by: Paul Mackerras --- drivers/block/viodasd.c | 49 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/block/viodasd.c b/drivers/block/viodasd.c index ec5a1b90a0a..e19ba4ebcd4 100644 --- a/drivers/block/viodasd.c +++ b/drivers/block/viodasd.c @@ -759,6 +759,8 @@ static struct vio_driver viodasd_driver = { } }; +static int need_delete_probe; + /* * Initialize the whole device driver. Handle module and non-module * versions @@ -773,46 +775,67 @@ static int __init viodasd_init(void) if (viopath_hostLp == HvLpIndexInvalid) { printk(VIOD_KERN_WARNING "invalid hosting partition\n"); - return -EIO; + rc = -EIO; + goto early_fail; } printk(VIOD_KERN_INFO "vers " VIOD_VERS ", hosting partition %d\n", viopath_hostLp); /* register the block device */ - if (register_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME)) { + rc = register_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME); + if (rc) { printk(VIOD_KERN_WARNING "Unable to get major number %d for %s\n", VIODASD_MAJOR, VIOD_GENHD_NAME); - return -EIO; + goto early_fail; } /* Actually open the path to the hosting partition */ - if (viopath_open(viopath_hostLp, viomajorsubtype_blockio, - VIOMAXREQ + 2)) { + rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, + VIOMAXREQ + 2); + if (rc) { printk(VIOD_KERN_WARNING "error opening path to host partition %d\n", viopath_hostLp); - unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME); - return -EIO; + goto unregister_blk; } /* Initialize our request handler */ vio_setHandler(viomajorsubtype_blockio, handle_block_event); rc = vio_register_driver(&viodasd_driver); - if (rc == 0) - driver_create_file(&viodasd_driver.driver, &driver_attr_probe); + if (rc) { + printk(VIOD_KERN_WARNING "vio_register_driver failed\n"); + goto unset_handler; + } + + /* + * If this call fails, it just means that we cannot dynamically + * add virtual disks, but the driver will still work fine for + * all existing disk, so ignore the failure. + */ + if (!driver_create_file(&viodasd_driver.driver, &driver_attr_probe)) + need_delete_probe = 1; + + return 0; + +unset_handler: + vio_clearHandler(viomajorsubtype_blockio); + viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2); +unregister_blk: + unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME); +early_fail: return rc; } module_init(viodasd_init); -void viodasd_exit(void) +void __exit viodasd_exit(void) { - driver_remove_file(&viodasd_driver.driver, &driver_attr_probe); + if (need_delete_probe) + driver_remove_file(&viodasd_driver.driver, &driver_attr_probe); vio_unregister_driver(&viodasd_driver); vio_clearHandler(viomajorsubtype_blockio); - unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME); viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2); + unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME); } - module_exit(viodasd_exit); -- cgit v1.2.3 From 533462df56db99ceba4b4124b69469aa2a46a8de Mon Sep 17 00:00:00 2001 From: Kalle Pokki Date: Mon, 13 Nov 2006 11:22:30 +0300 Subject: [POWERPC] CPM_UART: Fix inconsistency of function definition The below hunk was missed from the recent patch, and now, there are somewhat inconsistent definitions: in cpm_uart.h: int __init cpm_uart_init_portdesc(void); in cpm_uart_cpm1.c: int __init cpm_uart_init_portdesc(void) { } in cpm_uart_cpm2.c: int cpm_uart_init_portdesc(void) { } Signed-off-by: Kalle Pokki Signed-off-by: Vitaly Bordug Signed-off-by: Paul Mackerras --- drivers/serial/cpm_uart/cpm_uart_cpm2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c index b691d3e1475..787a8f13467 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.c @@ -282,7 +282,7 @@ void cpm_uart_freebuf(struct uart_cpm_port *pinfo) } /* Setup any dynamic params in the uart desc */ -int cpm_uart_init_portdesc(void) +int __init cpm_uart_init_portdesc(void) { #if defined(CONFIG_SERIAL_CPM_SMC1) || defined(CONFIG_SERIAL_CPM_SMC2) u32 addr; -- cgit v1.2.3 From 3e00a5aec3d6af687e37f4e7482f5c7ecdcabd0b Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 16 Nov 2006 14:03:33 +1100 Subject: [POWERPC] Xserve cpu-meter driver This is a small driver for the Xserve G5 CPU-meter blue LEDs on the front-panel. It might work on the Xserve G4 as well though that was not tested. It's pretty basic and could use some improvements if somebody cares doing them. :) Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Paul Mackerras --- drivers/macintosh/Kconfig | 7 + drivers/macintosh/Makefile | 1 + drivers/macintosh/rack-meter.c | 612 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 620 insertions(+) create mode 100644 drivers/macintosh/rack-meter.c (limited to 'drivers') diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index 7f8477d3a66..92ccee85e2a 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig @@ -228,4 +228,11 @@ config ANSLCD tristate "Support for ANS LCD display" depends on ADB_CUDA && PPC_PMAC +config PMAC_RACKMETER + tristate "Support for Apple XServe front panel LEDs" + depends on PPC_PMAC + help + This driver procides some support to control the front panel + blue LEDs "vu-meter" of the XServer macs. + endmenu diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile index b53d45f87b0..2dfc3f4eaf4 100644 --- a/drivers/macintosh/Makefile +++ b/drivers/macintosh/Makefile @@ -42,3 +42,4 @@ obj-$(CONFIG_WINDFARM_PM112) += windfarm_pm112.o windfarm_smu_sat.o \ windfarm_smu_sensors.o \ windfarm_max6690_sensor.o \ windfarm_lm75_sensor.o windfarm_pid.o +obj-$(CONFIG_PMAC_RACKMETER) += rack-meter.o diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c new file mode 100644 index 00000000000..f1b6f563673 --- /dev/null +++ b/drivers/macintosh/rack-meter.c @@ -0,0 +1,612 @@ +/* + * RackMac vu-meter driver + * + * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp. + * + * + * Released under the term of the GNU GPL v2. + * + * Support the CPU-meter LEDs of the Xserve G5 + * + * TODO: Implement PWM to do variable intensity and provide userland + * interface for fun. Also, the CPU-meter could be made nicer by being + * a bit less "immediate" but giving instead a more average load over + * time. Patches welcome :-) + * + */ +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Number of samples in a sample buffer */ +#define SAMPLE_COUNT 256 + +/* CPU meter sampling rate in ms */ +#define CPU_SAMPLING_RATE 250 + +struct rackmeter_dma { + struct dbdma_cmd cmd[4] ____cacheline_aligned; + u32 mark ____cacheline_aligned; + u32 buf1[SAMPLE_COUNT] ____cacheline_aligned; + u32 buf2[SAMPLE_COUNT] ____cacheline_aligned; +} ____cacheline_aligned; + +struct rackmeter_cpu { + struct work_struct sniffer; + cputime64_t prev_wall; + cputime64_t prev_idle; + int zero; +} ____cacheline_aligned; + +struct rackmeter { + struct macio_dev *mdev; + unsigned int irq; + struct device_node *i2s; + u8 *ubuf; + struct dbdma_regs __iomem *dma_regs; + void __iomem *i2s_regs; + dma_addr_t dma_buf_p; + struct rackmeter_dma *dma_buf_v; + int stale_irq; + struct rackmeter_cpu cpu[2]; + int paused; + struct mutex sem; +}; + +/* To be set as a tunable */ +static int rackmeter_ignore_nice; + +/* This GPIO is whacked by the OS X driver when initializing */ +#define RACKMETER_MAGIC_GPIO 0x78 + +/* This is copied from cpufreq_ondemand, maybe we should put it in + * a common header somewhere + */ +static inline cputime64_t get_cpu_idle_time(unsigned int cpu) +{ + cputime64_t retval; + + retval = cputime64_add(kstat_cpu(cpu).cpustat.idle, + kstat_cpu(cpu).cpustat.iowait); + + if (rackmeter_ignore_nice) + retval = cputime64_add(retval, kstat_cpu(cpu).cpustat.nice); + + return retval; +} + +static void rackmeter_setup_i2s(struct rackmeter *rm) +{ + struct macio_chip *macio = rm->mdev->bus->chip; + + /* First whack magic GPIO */ + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, RACKMETER_MAGIC_GPIO, 5); + + + /* Call feature code to enable the sound channel and the proper + * clock sources + */ + pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, rm->i2s, 0, 1); + + /* Power i2s and stop i2s clock. We whack MacIO FCRs directly for now. + * This is a bit racy, thus we should add new platform functions to + * handle that. snd-aoa needs that too + */ + MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_ENABLE); + MACIO_BIC(KEYLARGO_FCR1, KL1_I2S0_CLK_ENABLE_BIT); + (void)MACIO_IN32(KEYLARGO_FCR1); + udelay(10); + + /* Then setup i2s. For now, we use the same magic value that + * the OS X driver seems to use. We might want to play around + * with the clock divisors later + */ + out_le32(rm->i2s_regs + 0x10, 0x01fa0000); + (void)in_le32(rm->i2s_regs + 0x10); + udelay(10); + + /* Fully restart i2s*/ + MACIO_BIS(KEYLARGO_FCR1, KL1_I2S0_CELL_ENABLE | + KL1_I2S0_CLK_ENABLE_BIT); + (void)MACIO_IN32(KEYLARGO_FCR1); + udelay(10); +} + +static void rackmeter_set_default_pattern(struct rackmeter *rm) +{ + int i; + + for (i = 0; i < 16; i++) { + if (i < 8) + rm->ubuf[i] = (i & 1) * 255; + else + rm->ubuf[i] = ((~i) & 1) * 255; + } +} + +static void rackmeter_do_pause(struct rackmeter *rm, int pause) +{ + struct rackmeter_dma *rdma = rm->dma_buf_v; + + pr_debug("rackmeter: %s\n", pause ? "paused" : "started"); + + rm->paused = pause; + if (pause) { + DBDMA_DO_STOP(rm->dma_regs); + return; + } + memset(rdma->buf1, 0, SAMPLE_COUNT & sizeof(u32)); + memset(rdma->buf2, 0, SAMPLE_COUNT & sizeof(u32)); + + rm->dma_buf_v->mark = 0; + + mb(); + out_le32(&rm->dma_regs->cmdptr_hi, 0); + out_le32(&rm->dma_regs->cmdptr, rm->dma_buf_p); + out_le32(&rm->dma_regs->control, (RUN << 16) | RUN); +} + +static void rackmeter_setup_dbdma(struct rackmeter *rm) +{ + struct rackmeter_dma *db = rm->dma_buf_v; + struct dbdma_cmd *cmd = db->cmd; + + /* Make sure dbdma is reset */ + DBDMA_DO_RESET(rm->dma_regs); + + pr_debug("rackmeter: mark offset=0x%lx\n", + offsetof(struct rackmeter_dma, mark)); + pr_debug("rackmeter: buf1 offset=0x%lx\n", + offsetof(struct rackmeter_dma, buf1)); + pr_debug("rackmeter: buf2 offset=0x%lx\n", + offsetof(struct rackmeter_dma, buf2)); + + /* Prepare 4 dbdma commands for the 2 buffers */ + memset(cmd, 0, 4 * sizeof(struct dbdma_cmd)); + st_le16(&cmd->req_count, 4); + st_le16(&cmd->command, STORE_WORD | INTR_ALWAYS | KEY_SYSTEM); + st_le32(&cmd->phy_addr, rm->dma_buf_p + + offsetof(struct rackmeter_dma, mark)); + st_le32(&cmd->cmd_dep, 0x02000000); + cmd++; + + st_le16(&cmd->req_count, SAMPLE_COUNT * 4); + st_le16(&cmd->command, OUTPUT_MORE); + st_le32(&cmd->phy_addr, rm->dma_buf_p + + offsetof(struct rackmeter_dma, buf1)); + cmd++; + + st_le16(&cmd->req_count, 4); + st_le16(&cmd->command, STORE_WORD | INTR_ALWAYS | KEY_SYSTEM); + st_le32(&cmd->phy_addr, rm->dma_buf_p + + offsetof(struct rackmeter_dma, mark)); + st_le32(&cmd->cmd_dep, 0x01000000); + cmd++; + + st_le16(&cmd->req_count, SAMPLE_COUNT * 4); + st_le16(&cmd->command, OUTPUT_MORE | BR_ALWAYS); + st_le32(&cmd->phy_addr, rm->dma_buf_p + + offsetof(struct rackmeter_dma, buf2)); + st_le32(&cmd->cmd_dep, rm->dma_buf_p); + + rackmeter_do_pause(rm, 0); +} + +static void rackmeter_do_timer(void *data) +{ + struct rackmeter *rm = data; + unsigned int cpu = smp_processor_id(); + struct rackmeter_cpu *rcpu = &rm->cpu[cpu]; + cputime64_t cur_jiffies, total_idle_ticks; + unsigned int total_ticks, idle_ticks; + int i, offset, load, cumm, pause; + + cur_jiffies = jiffies64_to_cputime64(get_jiffies_64()); + total_ticks = (unsigned int)cputime64_sub(cur_jiffies, + rcpu->prev_wall); + rcpu->prev_wall = cur_jiffies; + + total_idle_ticks = get_cpu_idle_time(cpu); + idle_ticks = (unsigned int) cputime64_sub(total_idle_ticks, + rcpu->prev_idle); + rcpu->prev_idle = total_idle_ticks; + + /* We do a very dumb calculation to update the LEDs for now, + * we'll do better once we have actual PWM implemented + */ + load = (9 * (total_ticks - idle_ticks)) / total_ticks; + + offset = cpu << 3; + cumm = 0; + for (i = 0; i < 8; i++) { + u8 ub = (load > i) ? 0xff : 0; + rm->ubuf[i + offset] = ub; + cumm |= ub; + } + rcpu->zero = (cumm == 0); + + /* Now check if LEDs are all 0, we can stop DMA */ + pause = (rm->cpu[0].zero && rm->cpu[1].zero); + if (pause != rm->paused) { + mutex_lock(&rm->sem); + pause = (rm->cpu[0].zero && rm->cpu[1].zero); + rackmeter_do_pause(rm, pause); + mutex_unlock(&rm->sem); + } + schedule_delayed_work_on(cpu, &rcpu->sniffer, + msecs_to_jiffies(CPU_SAMPLING_RATE)); +} + +static void __devinit rackmeter_init_cpu_sniffer(struct rackmeter *rm) +{ + unsigned int cpu; + + /* This driver works only with 1 or 2 CPUs numbered 0 and 1, + * but that's really all we have on Apple Xserve. It doesn't + * play very nice with CPU hotplug neither but we don't do that + * on those machines yet + */ + + INIT_WORK(&rm->cpu[0].sniffer, rackmeter_do_timer, rm); + INIT_WORK(&rm->cpu[1].sniffer, rackmeter_do_timer, rm); + + for_each_online_cpu(cpu) { + struct rackmeter_cpu *rcpu; + + if (cpu > 1) + continue; + rcpu = &rm->cpu[cpu];; + rcpu->prev_idle = get_cpu_idle_time(cpu); + rcpu->prev_wall = jiffies64_to_cputime64(get_jiffies_64()); + schedule_delayed_work_on(cpu, &rm->cpu[cpu].sniffer, + msecs_to_jiffies(CPU_SAMPLING_RATE)); + } +} + +static void __devexit rackmeter_stop_cpu_sniffer(struct rackmeter *rm) +{ + cancel_rearming_delayed_work(&rm->cpu[0].sniffer); + cancel_rearming_delayed_work(&rm->cpu[1].sniffer); +} + +static int rackmeter_setup(struct rackmeter *rm) +{ + pr_debug("rackmeter: setting up i2s..\n"); + rackmeter_setup_i2s(rm); + + pr_debug("rackmeter: setting up default pattern..\n"); + rackmeter_set_default_pattern(rm); + + pr_debug("rackmeter: setting up dbdma..\n"); + rackmeter_setup_dbdma(rm); + + pr_debug("rackmeter: start CPU measurements..\n"); + rackmeter_init_cpu_sniffer(rm); + + printk(KERN_INFO "RackMeter initialized\n"); + + return 0; +} + +/* XXX FIXME: No PWM yet, this is 0/1 */ +static u32 rackmeter_calc_sample(struct rackmeter *rm, unsigned int index) +{ + int led; + u32 sample = 0; + + for (led = 0; led < 16; led++) { + sample >>= 1; + sample |= ((rm->ubuf[led] >= 0x80) << 15); + } + return (sample << 17) | (sample >> 15); +} + +static irqreturn_t rackmeter_irq(int irq, void *arg) +{ + struct rackmeter *rm = arg; + struct rackmeter_dma *db = rm->dma_buf_v; + unsigned int mark, i; + u32 *buf; + + /* Flush PCI buffers with an MMIO read. Maybe we could actually + * check the status one day ... in case things go wrong, though + * this never happened to me + */ + (void)in_le32(&rm->dma_regs->status); + + /* Make sure the CPU gets us in order */ + rmb(); + + /* Read mark */ + mark = db->mark; + if (mark != 1 && mark != 2) { + printk(KERN_WARNING "rackmeter: Incorrect DMA mark 0x%08x\n", + mark); + /* We allow for 3 errors like that (stale DBDMA irqs) */ + if (++rm->stale_irq > 3) { + printk(KERN_ERR "rackmeter: Too many errors," + " stopping DMA\n"); + DBDMA_DO_RESET(rm->dma_regs); + } + return IRQ_HANDLED; + } + + /* Next buffer we need to fill is mark value */ + buf = mark == 1 ? db->buf1 : db->buf2; + + /* Fill it now. This routine converts the 8 bits depth sample array + * into the PWM bitmap for each LED. + */ + for (i = 0; i < SAMPLE_COUNT; i++) + buf[i] = rackmeter_calc_sample(rm, i); + + + return IRQ_HANDLED; +} + +static int __devinit rackmeter_probe(struct macio_dev* mdev, + const struct of_device_id *match) +{ + struct device_node *i2s = NULL, *np = NULL; + struct rackmeter *rm = NULL; + struct resource ri2s, rdma; + int rc = -ENODEV; + + pr_debug("rackmeter_probe()\n"); + + /* Get i2s-a node */ + while ((i2s = of_get_next_child(mdev->ofdev.node, i2s)) != NULL) + if (strcmp(i2s->name, "i2s-a") == 0) + break; + if (i2s == NULL) { + pr_debug(" i2s-a child not found\n"); + goto bail; + } + /* Get lightshow or virtual sound */ + while ((np = of_get_next_child(i2s, np)) != NULL) { + if (strcmp(np->name, "lightshow") == 0) + break; + if ((strcmp(np->name, "sound") == 0) && + get_property(np, "virtual", NULL) != NULL) + break; + } + if (np == NULL) { + pr_debug(" lightshow or sound+virtual child not found\n"); + goto bail; + } + + /* Create and initialize our instance data */ + rm = kzalloc(sizeof(struct rackmeter), GFP_KERNEL); + if (rm == NULL) { + printk(KERN_ERR "rackmeter: failed to allocate memory !\n"); + rc = -ENOMEM; + goto bail_release; + } + rm->mdev = mdev; + rm->i2s = i2s; + mutex_init(&rm->sem); + dev_set_drvdata(&mdev->ofdev.dev, rm); + /* Check resources availability. We need at least resource 0 and 1 */ +#if 0 /* Use that when i2s-a is finally an mdev per-se */ + if (macio_resource_count(mdev) < 2 || macio_irq_count(mdev) < 2) { + printk(KERN_ERR + "rackmeter: found match but lacks resources: %s" + " (%d resources, %d interrupts)\n", + mdev->ofdev.node->full_name); + rc = -ENXIO; + goto bail_free; + } + if (macio_request_resources(mdev, "rackmeter")) { + printk(KERN_ERR + "rackmeter: failed to request resources: %s\n", + mdev->ofdev.node->full_name); + rc = -EBUSY; + goto bail_free; + } + rm->irq = macio_irq(mdev, 1); +#else + rm->irq = irq_of_parse_and_map(i2s, 1); + if (rm->irq == NO_IRQ || + of_address_to_resource(i2s, 0, &ri2s) || + of_address_to_resource(i2s, 1, &rdma)) { + printk(KERN_ERR + "rackmeter: found match but lacks resources: %s", + mdev->ofdev.node->full_name); + rc = -ENXIO; + goto bail_free; + } +#endif + + pr_debug(" i2s @0x%08x\n", (unsigned int)ri2s.start); + pr_debug(" dma @0x%08x\n", (unsigned int)rdma.start); + pr_debug(" irq %d\n", rm->irq); + + rm->ubuf = (u8 *)__get_free_page(GFP_KERNEL); + if (rm->ubuf == NULL) { + printk(KERN_ERR + "rackmeter: failed to allocate samples page !\n"); + rc = -ENOMEM; + goto bail_release; + } + + rm->dma_buf_v = dma_alloc_coherent(&macio_get_pci_dev(mdev)->dev, + sizeof(struct rackmeter_dma), + &rm->dma_buf_p, GFP_KERNEL); + if (rm->dma_buf_v == NULL) { + printk(KERN_ERR + "rackmeter: failed to allocate dma buffer !\n"); + rc = -ENOMEM; + goto bail_free_samples; + } +#if 0 + rm->i2s_regs = ioremap(macio_resource_start(mdev, 0), 0x1000); +#else + rm->i2s_regs = ioremap(ri2s.start, 0x1000); +#endif + if (rm->i2s_regs == NULL) { + printk(KERN_ERR + "rackmeter: failed to map i2s registers !\n"); + rc = -ENXIO; + goto bail_free_dma; + } +#if 0 + rm->dma_regs = ioremap(macio_resource_start(mdev, 1), 0x100); +#else + rm->dma_regs = ioremap(rdma.start, 0x100); +#endif + if (rm->dma_regs == NULL) { + printk(KERN_ERR + "rackmeter: failed to map dma registers !\n"); + rc = -ENXIO; + goto bail_unmap_i2s; + } + + rc = rackmeter_setup(rm); + if (rc) { + printk(KERN_ERR + "rackmeter: failed to initialize !\n"); + rc = -ENXIO; + goto bail_unmap_dma; + } + + rc = request_irq(rm->irq, rackmeter_irq, 0, "rackmeter", rm); + if (rc != 0) { + printk(KERN_ERR + "rackmeter: failed to request interrupt !\n"); + goto bail_stop_dma; + } + of_node_put(np); + return 0; + + bail_stop_dma: + DBDMA_DO_RESET(rm->dma_regs); + bail_unmap_dma: + iounmap(rm->dma_regs); + bail_unmap_i2s: + iounmap(rm->i2s_regs); + bail_free_dma: + dma_free_coherent(&macio_get_pci_dev(mdev)->dev, + sizeof(struct rackmeter_dma), + rm->dma_buf_v, rm->dma_buf_p); + bail_free_samples: + free_page((unsigned long)rm->ubuf); + bail_release: +#if 0 + macio_release_resources(mdev); +#endif + bail_free: + kfree(rm); + bail: + of_node_put(i2s); + of_node_put(np); + dev_set_drvdata(&mdev->ofdev.dev, NULL); + return rc; +} + +static int __devexit rackmeter_remove(struct macio_dev* mdev) +{ + struct rackmeter *rm = dev_get_drvdata(&mdev->ofdev.dev); + + /* Stop CPU sniffer timer & work queues */ + rackmeter_stop_cpu_sniffer(rm); + + /* Clear reference to private data */ + dev_set_drvdata(&mdev->ofdev.dev, NULL); + + /* Stop/reset dbdma */ + DBDMA_DO_RESET(rm->dma_regs); + + /* Release the IRQ */ + free_irq(rm->irq, rm); + + /* Unmap registers */ + iounmap(rm->dma_regs); + iounmap(rm->i2s_regs); + + /* Free DMA */ + dma_free_coherent(&macio_get_pci_dev(mdev)->dev, + sizeof(struct rackmeter_dma), + rm->dma_buf_v, rm->dma_buf_p); + + /* Free samples */ + free_page((unsigned long)rm->ubuf); + +#if 0 + /* Release resources */ + macio_release_resources(mdev); +#endif + + /* Get rid of me */ + kfree(rm); + + return 0; +} + +static int rackmeter_shutdown(struct macio_dev* mdev) +{ + struct rackmeter *rm = dev_get_drvdata(&mdev->ofdev.dev); + + if (rm == NULL) + return -ENODEV; + + /* Stop CPU sniffer timer & work queues */ + rackmeter_stop_cpu_sniffer(rm); + + /* Stop/reset dbdma */ + DBDMA_DO_RESET(rm->dma_regs); + + return 0; +} + +static struct of_device_id rackmeter_match[] = { + { .name = "i2s" }, + { } +}; + +static struct macio_driver rackmeter_drv = { + .name = "rackmeter", + .owner = THIS_MODULE, + .match_table = rackmeter_match, + .probe = rackmeter_probe, + .remove = rackmeter_remove, + .shutdown = rackmeter_shutdown, +}; + + +static int __init rackmeter_init(void) +{ + pr_debug("rackmeter_init()\n"); + + return macio_register_driver(&rackmeter_drv); +} + +static void __exit rackmeter_exit(void) +{ + pr_debug("rackmeter_exit()\n"); + + macio_unregister_driver(&rackmeter_drv); +} + +module_init(rackmeter_init); +module_exit(rackmeter_exit); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Benjamin Herrenschmidt "); +MODULE_DESCRIPTION("RackMeter: Support vu-meter on XServe front panel"); -- cgit v1.2.3 From a3d4d6435b56eb0b6ff4f88e5a513cfccfb3e770 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Thu, 23 Nov 2006 00:47:00 +0100 Subject: [POWERPC] ps3: add ps3 platform system bus support Adds a PS3 system bus driver. This system bus is a virtual bus used to present the PS3 system devices in the LDM. Signed-off-by: Geoff Levand Signed-off-by: Arnd Bergmann --- drivers/Makefile | 1 + drivers/ps3/Makefile | 1 + drivers/ps3/system-bus.c | 358 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 360 insertions(+) create mode 100644 drivers/ps3/Makefile create mode 100644 drivers/ps3/system-bus.c (limited to 'drivers') diff --git a/drivers/Makefile b/drivers/Makefile index 4ac14dab307..a47d2483b7a 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -77,3 +77,4 @@ obj-$(CONFIG_CRYPTO) += crypto/ obj-$(CONFIG_SUPERH) += sh/ obj-$(CONFIG_GENERIC_TIME) += clocksource/ obj-$(CONFIG_DMA_ENGINE) += dma/ +obj-$(CONFIG_PS3) += ps3/ diff --git a/drivers/ps3/Makefile b/drivers/ps3/Makefile new file mode 100644 index 00000000000..b52d547b7a7 --- /dev/null +++ b/drivers/ps3/Makefile @@ -0,0 +1 @@ +obj-y += system-bus.o diff --git a/drivers/ps3/system-bus.c b/drivers/ps3/system-bus.c new file mode 100644 index 00000000000..e19992c4db4 --- /dev/null +++ b/drivers/ps3/system-bus.c @@ -0,0 +1,358 @@ +/* + * PS3 system bus driver. + * + * Copyright (C) 2006 Sony Computer Entertainment Inc. + * Copyright 2006 Sony Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define dump_mmio_region(_a) _dump_mmio_region(_a, __func__, __LINE__) +static void _dump_mmio_region(const struct ps3_mmio_region* r, + const char* func, int line) +{ + pr_debug("%s:%d: dev %u:%u\n", func, line, r->did.bus_id, + r->did.dev_id); + pr_debug("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr); + pr_debug("%s:%d: len %lxh\n", func, line, r->len); + pr_debug("%s:%d: lpar_addr %lxh\n", func, line, r->lpar_addr); +} + +int ps3_mmio_region_create(struct ps3_mmio_region *r) +{ + int result; + + result = lv1_map_device_mmio_region(r->did.bus_id, r->did.dev_id, + r->bus_addr, r->len, r->page_size, &r->lpar_addr); + + if (result) { + pr_debug("%s:%d: lv1_map_device_mmio_region failed: %s\n", + __func__, __LINE__, ps3_result(result)); + r->lpar_addr = r->len = r->bus_addr = 0; + } + + dump_mmio_region(r); + return result; +} + +int ps3_free_mmio_region(struct ps3_mmio_region *r) +{ + int result; + + result = lv1_unmap_device_mmio_region(r->did.bus_id, r->did.dev_id, + r->bus_addr); + + if (result) + pr_debug("%s:%d: lv1_unmap_device_mmio_region failed: %s\n", + __func__, __LINE__, ps3_result(result)); + + r->lpar_addr = r->len = r->bus_addr = 0; + return result; +} + +static int ps3_system_bus_match(struct device *_dev, + struct device_driver *_drv) +{ + int result; + struct ps3_system_bus_driver *drv = to_ps3_system_bus_driver(_drv); + struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + + result = dev->match_id == drv->match_id; + + pr_info("%s:%d: dev=%u(%s), drv=%u(%s): %s\n", __func__, __LINE__, + dev->match_id, dev->core.bus_id, drv->match_id, drv->core.name, + (result ? "match" : "miss")); + return result; +} + +static int ps3_system_bus_probe(struct device *_dev) +{ + int result; + struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_driver *drv = + to_ps3_system_bus_driver(_dev->driver); + + result = lv1_open_device(dev->did.bus_id, dev->did.dev_id, 0); + + if (result) { + pr_debug("%s:%d: lv1_open_device failed (%d)\n", + __func__, __LINE__, result); + result = -EACCES; + goto clean_none; + } + + if (dev->d_region->did.bus_id) { + result = ps3_dma_region_create(dev->d_region); + + if (result) { + pr_debug("%s:%d: ps3_dma_region_create failed (%d)\n", + __func__, __LINE__, result); + BUG_ON("check region type"); + result = -EINVAL; + goto clean_device; + } + } + + BUG_ON(!drv); + + if (drv->probe) + result = drv->probe(dev); + else + pr_info("%s:%d: %s no probe method\n", __func__, __LINE__, + dev->core.bus_id); + + if (result) { + pr_debug("%s:%d: drv->probe failed\n", __func__, __LINE__); + goto clean_dma; + } + + return result; + +clean_dma: + ps3_dma_region_free(dev->d_region); +clean_device: + lv1_close_device(dev->did.bus_id, dev->did.dev_id); +clean_none: + return result; +} + +static int ps3_system_bus_remove(struct device *_dev) +{ + struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + struct ps3_system_bus_driver *drv = + to_ps3_system_bus_driver(_dev->driver); + + if (drv->remove) + drv->remove(dev); + else + pr_info("%s:%d: %s no remove method\n", __func__, __LINE__, + dev->core.bus_id); + + ps3_dma_region_free(dev->d_region); + ps3_free_mmio_region(dev->m_region); + lv1_close_device(dev->did.bus_id, dev->did.dev_id); + + return 0; +} + +struct bus_type ps3_system_bus_type = { + .name = "ps3_system_bus", + .match = ps3_system_bus_match, + .probe = ps3_system_bus_probe, + .remove = ps3_system_bus_remove, +}; + +int __init ps3_system_bus_init(void) +{ + int result; + + result = bus_register(&ps3_system_bus_type); + BUG_ON(result); + return result; +} + +core_initcall(ps3_system_bus_init); + +/* Allocates a contiguous real buffer and creates mappings over it. + * Returns the virtual address of the buffer and sets dma_handle + * to the dma address (mapping) of the first page. + */ + +static void * ps3_alloc_coherent(struct device *_dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + int result; + struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + unsigned long virt_addr; + + BUG_ON(!dev->d_region->bus_addr); + + flag &= ~(__GFP_DMA | __GFP_HIGHMEM); + flag |= __GFP_ZERO; + + virt_addr = __get_free_pages(flag, get_order(size)); + + if (!virt_addr) { + pr_debug("%s:%d: get_free_pages failed\n", __func__, __LINE__); + goto clean_none; + } + + result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle); + + if (result) { + pr_debug("%s:%d: ps3_dma_map failed (%d)\n", + __func__, __LINE__, result); + BUG_ON("check region type"); + goto clean_alloc; + } + + return (void*)virt_addr; + +clean_alloc: + free_pages(virt_addr, get_order(size)); +clean_none: + dma_handle = NULL; + return NULL; +} + +static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr, + dma_addr_t dma_handle) +{ + struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + + ps3_dma_unmap(dev->d_region, dma_handle, size); + free_pages((unsigned long)vaddr, get_order(size)); +} + +/* Creates TCEs for a user provided buffer. The user buffer must be + * contiguous real kernel storage (not vmalloc). The address of the buffer + * passed here is the kernel (virtual) address of the buffer. The buffer + * need not be page aligned, the dma_addr_t returned will point to the same + * byte within the page as vaddr. + */ + +static dma_addr_t ps3_map_single(struct device *_dev, void *ptr, size_t size, + enum dma_data_direction direction) +{ + struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + int result; + unsigned long bus_addr; + + result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, + &bus_addr); + + if (result) { + pr_debug("%s:%d: ps3_dma_map failed (%d)\n", + __func__, __LINE__, result); + } + + return bus_addr; +} + +static void ps3_unmap_single(struct device *_dev, dma_addr_t dma_addr, + size_t size, enum dma_data_direction direction) +{ + struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + int result; + + result = ps3_dma_unmap(dev->d_region, dma_addr, size); + + if (result) { + pr_debug("%s:%d: ps3_dma_unmap failed (%d)\n", + __func__, __LINE__, result); + } +} + +static int ps3_map_sg(struct device *_dev, struct scatterlist *sg, int nents, + enum dma_data_direction direction) +{ +#if defined(CONFIG_PS3_DYNAMIC_DMA) + BUG_ON("do"); +#endif + return 0; +} + +static void ps3_unmap_sg(struct device *_dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction) +{ +#if defined(CONFIG_PS3_DYNAMIC_DMA) + BUG_ON("do"); +#endif +} + +static int ps3_dma_supported(struct device *_dev, u64 mask) +{ + return 1; +} + +static struct dma_mapping_ops ps3_dma_ops = { + .alloc_coherent = ps3_alloc_coherent, + .free_coherent = ps3_free_coherent, + .map_single = ps3_map_single, + .unmap_single = ps3_unmap_single, + .map_sg = ps3_map_sg, + .unmap_sg = ps3_unmap_sg, + .dma_supported = ps3_dma_supported +}; + +/** + * ps3_system_bus_release_device - remove a device from the system bus + */ + +static void ps3_system_bus_release_device(struct device *_dev) +{ + struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); + kfree(dev); +} + +/** + * ps3_system_bus_device_register - add a device to the system bus + * + * ps3_system_bus_device_register() expects the dev object to be allocated + * dynamically by the caller. The system bus takes ownership of the dev + * object and frees the object in ps3_system_bus_release_device(). + */ + +int ps3_system_bus_device_register(struct ps3_system_bus_device *dev) +{ + int result; + static unsigned int dev_count = 1; + + dev->core.parent = NULL; + dev->core.bus = &ps3_system_bus_type; + dev->core.release = ps3_system_bus_release_device; + + dev->core.archdata.of_node = NULL; + dev->core.archdata.dma_ops = &ps3_dma_ops; + dev->core.archdata.numa_node = 0; + + snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "sb_%02x", + dev_count++); + + pr_debug("%s:%d add %s\n", __func__, __LINE__, dev->core.bus_id); + + result = device_register(&dev->core); + return result; +} + +EXPORT_SYMBOL_GPL(ps3_system_bus_device_register); + +int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv) +{ + int result; + + drv->core.bus = &ps3_system_bus_type; + + result = driver_register(&drv->core); + return result; +} + +EXPORT_SYMBOL_GPL(ps3_system_bus_driver_register); + +void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv) +{ + driver_unregister(&drv->core); +} + +EXPORT_SYMBOL_GPL(ps3_system_bus_driver_unregister); -- cgit v1.2.3 From eb30c72026500f9efa9bb23ab2393d6a9e36c5e1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 27 Nov 2006 19:18:56 +0100 Subject: [POWERPC] ps3: Missed renames of CONFIG_PS3 to CONFIG_PPC_PS3 When renaming CONFIG_PS3 to CONFIG_PPC_PS3, a few occurrences have been missed. I also fixed up the alignment in arch/powerpc/platforms/Makefile. Signed-off-by: Geert Uytterhoeven Signed-off-by: Arnd Bergmann --- drivers/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/Makefile b/drivers/Makefile index a47d2483b7a..67711770b1d 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -77,4 +77,4 @@ obj-$(CONFIG_CRYPTO) += crypto/ obj-$(CONFIG_SUPERH) += sh/ obj-$(CONFIG_GENERIC_TIME) += clocksource/ obj-$(CONFIG_DMA_ENGINE) += dma/ -obj-$(CONFIG_PS3) += ps3/ +obj-$(CONFIG_PPC_PS3) += ps3/ -- cgit v1.2.3 From e22ba7e38144c1cccac5024cfd6ec88bb64d3e1f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 27 Nov 2006 19:18:57 +0100 Subject: [POWERPC] ps3: multiplatform build fixes A few code paths need to check whether or not they are running on the PS3's LV1 hypervisor before making hcalls. This introduces a new firmware feature bit for this, FW_FEATURE_PS3_LV1. Now when both PS3 and IBM_CELL_BLADE are enabled, but not PSERIES, FW_FEATURE_PS3_LV1 and FW_FEATURE_LPAR get enabled at compile time, which is a bug. The same problem can also happen for (PPC_ISERIES && !PPC_PSERIES && PPC_SOMETHING_ELSE). In order to solve this, I introduce a new CONFIG_PPC_NATIVE option that is set when at least one platform is selected that can run without a hypervisor and then turns the firmware feature check into a run-time option. The new cell oprofile support that was recently merged does not work on hypervisor based platforms like the PS3, therefore make it depend on PPC_CELL_NATIVE instead of PPC_CELL. This may change if we get oprofile support for PS3. Signed-off-by: Arnd Bergmann --- drivers/ps3/system-bus.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/ps3/system-bus.c b/drivers/ps3/system-bus.c index e19992c4db4..d79f949bcb2 100644 --- a/drivers/ps3/system-bus.c +++ b/drivers/ps3/system-bus.c @@ -27,6 +27,7 @@ #include #include #include +#include #define dump_mmio_region(_a) _dump_mmio_region(_a, __func__, __LINE__) static void _dump_mmio_region(const struct ps3_mmio_region* r, @@ -167,6 +168,9 @@ int __init ps3_system_bus_init(void) { int result; + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return 0; + result = bus_register(&ps3_system_bus_type); BUG_ON(result); return result; -- cgit v1.2.3 From 9b9129e73985fe31ee6fd6a5d9f04219d127c186 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Mon, 27 Nov 2006 14:21:01 -0700 Subject: [POWERPC] typo fix and whitespace cleanup on mpc52xx-uart driver Single typo fix and whitespace changes. In preparation for heavy changes to this driver when support for arch/powerpc is added. Since the driver will be changing significantly anyway, then may as well take the opportunity to clean it up first. Signed-off-by: Grant Likely Signed-off-by: Sylvain Munaut Signed-off-by: Paul Mackerras --- drivers/serial/mpc52xx_uart.c | 116 +++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 59 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 4f80c5b4a75..8e24133166a 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -1,6 +1,4 @@ /* - * drivers/serial/mpc52xx_uart.c - * * Driver for the PSC of the Freescale MPC52xx PSCs configured as UARTs. * * FIXME According to the usermanual the status bits in the status register @@ -14,18 +12,18 @@ * * * Maintainer : Sylvain Munaut - * + * * Some of the code has been inspired/copied from the 2.4 code written * by Dale Farnsworth . - * + * * Copyright (C) 2004-2005 Sylvain Munaut * Copyright (C) 2003 MontaVista, Software, Inc. - * + * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. */ - + /* Platform device Usage : * * Since PSCs can have multiple function, the correct driver for each one @@ -101,27 +99,27 @@ static irqreturn_t mpc52xx_uart_int(int irq,void *dev_id); /* UART operations */ /* ======================================================================== */ -static unsigned int +static unsigned int mpc52xx_uart_tx_empty(struct uart_port *port) { int status = in_be16(&PSC(port)->mpc52xx_psc_status); return (status & MPC52xx_PSC_SR_TXEMP) ? TIOCSER_TEMT : 0; } -static void +static void mpc52xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { /* Not implemented */ } -static unsigned int +static unsigned int mpc52xx_uart_get_mctrl(struct uart_port *port) { /* Not implemented */ return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; } -static void +static void mpc52xx_uart_stop_tx(struct uart_port *port) { /* port->lock taken by caller */ @@ -129,7 +127,7 @@ mpc52xx_uart_stop_tx(struct uart_port *port) out_be16(&PSC(port)->mpc52xx_psc_imr,port->read_status_mask); } -static void +static void mpc52xx_uart_start_tx(struct uart_port *port) { /* port->lock taken by caller */ @@ -137,12 +135,12 @@ mpc52xx_uart_start_tx(struct uart_port *port) out_be16(&PSC(port)->mpc52xx_psc_imr,port->read_status_mask); } -static void +static void mpc52xx_uart_send_xchar(struct uart_port *port, char ch) { unsigned long flags; spin_lock_irqsave(&port->lock, flags); - + port->x_char = ch; if (ch) { /* Make sure tx interrupts are on */ @@ -150,7 +148,7 @@ mpc52xx_uart_send_xchar(struct uart_port *port, char ch) port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY; out_be16(&PSC(port)->mpc52xx_psc_imr,port->read_status_mask); } - + spin_unlock_irqrestore(&port->lock, flags); } @@ -178,7 +176,7 @@ mpc52xx_uart_break_ctl(struct uart_port *port, int ctl) out_8(&PSC(port)->command,MPC52xx_PSC_START_BRK); else out_8(&PSC(port)->command,MPC52xx_PSC_STOP_BRK); - + spin_unlock_irqrestore(&port->lock, flags); } @@ -197,11 +195,11 @@ mpc52xx_uart_startup(struct uart_port *port) /* Reset/activate the port, clear and enable interrupts */ out_8(&psc->command,MPC52xx_PSC_RST_RX); out_8(&psc->command,MPC52xx_PSC_RST_TX); - + out_be32(&psc->sicr,0); /* UART mode DCD ignored */ out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); /* /16 prescaler on */ - + out_8(&psc->rfcntl, 0x00); out_be16(&psc->rfalarm, 0x1ff); out_8(&psc->tfcntl, 0x07); @@ -209,10 +207,10 @@ mpc52xx_uart_startup(struct uart_port *port) port->read_status_mask |= MPC52xx_PSC_IMR_RXRDY | MPC52xx_PSC_IMR_TXRDY; out_be16(&psc->mpc52xx_psc_imr,port->read_status_mask); - + out_8(&psc->command,MPC52xx_PSC_TX_ENABLE); out_8(&psc->command,MPC52xx_PSC_RX_ENABLE); - + return 0; } @@ -220,19 +218,19 @@ static void mpc52xx_uart_shutdown(struct uart_port *port) { struct mpc52xx_psc __iomem *psc = PSC(port); - + /* Shut down the port, interrupt and all */ out_8(&psc->command,MPC52xx_PSC_RST_RX); out_8(&psc->command,MPC52xx_PSC_RST_TX); - - port->read_status_mask = 0; + + port->read_status_mask = 0; out_be16(&psc->mpc52xx_psc_imr,port->read_status_mask); /* Release interrupt */ free_irq(port->irq, port); } -static void +static void mpc52xx_uart_set_termios(struct uart_port *port, struct termios *new, struct termios *old) { @@ -241,10 +239,10 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct termios *new, unsigned char mr1, mr2; unsigned short ctr; unsigned int j, baud, quot; - + /* Prepare what we're gonna write */ mr1 = 0; - + switch (new->c_cflag & CSIZE) { case CS5: mr1 |= MPC52xx_PSC_MODE_5_BITS; break; @@ -261,8 +259,8 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct termios *new, MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN; } else mr1 |= MPC52xx_PSC_MODE_PARNONE; - - + + mr2 = 0; if (new->c_cflag & CSTOPB) @@ -276,7 +274,7 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct termios *new, baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16); quot = uart_get_divisor(port, baud); ctr = quot & 0xffff; - + /* Get the lock */ spin_lock_irqsave(&port->lock, flags); @@ -290,14 +288,14 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct termios *new, * boot for the console, all stuff is not yet ready to receive at that * time and that just makes the kernel oops */ /* while (j-- && mpc52xx_uart_int_rx_chars(port)); */ - while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP) && + while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP) && --j) udelay(1); if (!j) printk( KERN_ERR "mpc52xx_uart.c: " "Unable to flush RX & TX fifos in-time in set_termios." - "Some chars may have been lost.\n" ); + "Some chars may have been lost.\n" ); /* Reset the TX & RX */ out_8(&psc->command,MPC52xx_PSC_RST_RX); @@ -309,7 +307,7 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct termios *new, out_8(&psc->mode,mr2); out_8(&psc->ctur,ctr >> 8); out_8(&psc->ctlr,ctr & 0xff); - + /* Reenable TX & RX */ out_8(&psc->command,MPC52xx_PSC_TX_ENABLE); out_8(&psc->command,MPC52xx_PSC_RX_ENABLE); @@ -373,7 +371,7 @@ mpc52xx_uart_verify_port(struct uart_port *port, struct serial_struct *ser) if ( (ser->irq != port->irq) || (ser->io_type != SERIAL_IO_MEM) || - (ser->baud_base != port->uartclk) || + (ser->baud_base != port->uartclk) || (ser->iomem_base != (void*)port->mapbase) || (ser->hub6 != 0 ) ) return -EINVAL; @@ -404,11 +402,11 @@ static struct uart_ops mpc52xx_uart_ops = { .verify_port = mpc52xx_uart_verify_port }; - + /* ======================================================================== */ /* Interrupt handling */ /* ======================================================================== */ - + static inline int mpc52xx_uart_int_rx_chars(struct uart_port *port) { @@ -435,11 +433,11 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) flag = TTY_NORMAL; port->icount.rx++; - + if ( status & (MPC52xx_PSC_SR_PE | MPC52xx_PSC_SR_FE | MPC52xx_PSC_SR_RB) ) { - + if (status & MPC52xx_PSC_SR_RB) { flag = TTY_BREAK; uart_handle_break(port); @@ -464,7 +462,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) } tty_flip_buffer_push(tty); - + return in_be16(&PSC(port)->mpc52xx_psc_status) & MPC52xx_PSC_SR_RXRDY; } @@ -509,25 +507,25 @@ mpc52xx_uart_int_tx_chars(struct uart_port *port) return 1; } -static irqreturn_t +static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id) { struct uart_port *port = dev_id; unsigned long pass = ISR_PASS_LIMIT; unsigned int keepgoing; unsigned short status; - + spin_lock(&port->lock); - + /* While we have stuff to do, we continue */ do { /* If we don't find anything to do, we stop */ - keepgoing = 0; - + keepgoing = 0; + /* Read status */ status = in_be16(&PSC(port)->mpc52xx_psc_isr); status &= port->read_status_mask; - + /* Do we need to receive chars ? */ /* For this RX interrupts must be on and some chars waiting */ if ( status & MPC52xx_PSC_IMR_RXRDY ) @@ -537,15 +535,15 @@ mpc52xx_uart_int(int irq, void *dev_id) /* For this, TX must be ready and TX interrupt enabled */ if ( status & MPC52xx_PSC_IMR_TXRDY ) keepgoing |= mpc52xx_uart_int_tx_chars(port); - + /* Limit number of iteration */ if ( !(--pass) ) keepgoing = 0; } while (keepgoing); - + spin_unlock(&port->lock); - + return IRQ_HANDLED; } @@ -566,7 +564,7 @@ mpc52xx_console_get_options(struct uart_port *port, /* Read the mode registers */ out_8(&psc->command,MPC52xx_PSC_SEL_MODE_REG_1); mr1 = in_8(&psc->mode); - + /* CT{U,L}R are write-only ! */ *baud = __res.bi_baudrate ? __res.bi_baudrate : CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD; @@ -579,26 +577,26 @@ mpc52xx_console_get_options(struct uart_port *port, case MPC52xx_PSC_MODE_8_BITS: default: *bits = 8; } - + if (mr1 & MPC52xx_PSC_MODE_PARNONE) *parity = 'n'; else *parity = mr1 & MPC52xx_PSC_MODE_PARODD ? 'o' : 'e'; } -static void +static void mpc52xx_console_write(struct console *co, const char *s, unsigned int count) { struct uart_port *port = &mpc52xx_uart_ports[co->index]; struct mpc52xx_psc __iomem *psc = PSC(port); unsigned int i, j; - + /* Disable interrupts */ out_be16(&psc->mpc52xx_psc_imr, 0); /* Wait the TX buffer to be empty */ - j = 5000000; /* Maximum wait */ - while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP) && + j = 5000000; /* Maximum wait */ + while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP) && --j) udelay(1); @@ -607,13 +605,13 @@ mpc52xx_console_write(struct console *co, const char *s, unsigned int count) /* Line return handling */ if (*s == '\n') out_8(&psc->mpc52xx_psc_buffer_8, '\r'); - + /* Send the char */ out_8(&psc->mpc52xx_psc_buffer_8, *s); /* Wait the TX buffer to be empty */ - j = 20000; /* Maximum wait */ - while (!(in_be16(&psc->mpc52xx_psc_status) & + j = 20000; /* Maximum wait */ + while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP) && --j) udelay(1); } @@ -634,7 +632,7 @@ mpc52xx_console_setup(struct console *co, char *options) if (co->index < 0 || co->index >= MPC52xx_PSC_MAXNUM) return -EINVAL; - + /* Basic port init. Needed since we use some uart_??? func before * real init for early access */ spin_lock_init(&port->lock); @@ -669,8 +667,8 @@ static struct console mpc52xx_console = { .data = &mpc52xx_uart_driver, }; - -static int __init + +static int __init mpc52xx_console_init(void) { register_console(&mpc52xx_console); @@ -771,7 +769,7 @@ mpc52xx_uart_suspend(struct platform_device *dev, pm_message_t state) { struct uart_port *port = (struct uart_port *) platform_get_drvdata(dev); - if (sport) + if (port) uart_suspend_port(&mpc52xx_uart_driver, port); return 0; -- cgit v1.2.3 From b9272dfdfe6b13120144eabf79d562d9a29ad5ce Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Mon, 27 Nov 2006 14:21:02 -0700 Subject: [POWERPC] Add of_platform_bus support to mpc52xx psc uart driver Needed to support mpc52xx boards in arch/powerpc. This patch retains the platform_bus support when compiling for arch/ppc, but uses the of_platform bindings for arch/powerpc. Signed-off-by: Grant Likely Signed-off-by: Sylvain Munaut Signed-off-by: Paul Mackerras --- drivers/serial/mpc52xx_uart.c | 353 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 337 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 8e24133166a..6dd579ed977 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -16,7 +16,9 @@ * Some of the code has been inspired/copied from the 2.4 code written * by Dale Farnsworth . * - * Copyright (C) 2004-2005 Sylvain Munaut + * Copyright (C) 2006 Secret Lab Technologies Ltd. + * Grant Likely + * Copyright (C) 2004-2006 Sylvain Munaut * Copyright (C) 2003 MontaVista, Software, Inc. * * This file is licensed under the terms of the GNU General Public License @@ -42,7 +44,24 @@ * will be mapped to. */ -#include +/* OF Platform device Usage : + * + * This driver is only used for PSCs configured in uart mode. The device + * tree will have a node for each PSC in uart mode w/ device_type = "serial" + * and "mpc52xx-psc-uart" in the compatible string + * + * By default, PSC devices are enumerated in the order they are found. However + * a particular PSC number can be forces by adding 'device_no = ' + * to the device node. + * + * The driver init all necessary registers to place the PSC in uart mode without + * DCD. However, the pin multiplexing aren't changed and should be set either + * by the bootloader or in the platform init code. + */ + +#undef DEBUG + +#include #include #include #include @@ -52,6 +71,12 @@ #include #include +#if defined(CONFIG_PPC_MERGE) +#include +#else +#include +#endif + #include #include @@ -78,6 +103,12 @@ static struct uart_port mpc52xx_uart_ports[MPC52xx_PSC_MAXNUM]; * it's cleared, then a memset(...,0,...) should be added to * the console_init */ +#if defined(CONFIG_PPC_MERGE) +/* lookup table for matching device nodes to index numbers */ +static struct device_node *mpc52xx_uart_nodes[MPC52xx_PSC_MAXNUM]; + +static void mpc52xx_uart_of_enumerate(void); +#endif #define PSC(port) ((struct mpc52xx_psc __iomem *)((port)->membase)) @@ -94,6 +125,14 @@ static irqreturn_t mpc52xx_uart_int(int irq,void *dev_id); #define uart_console(port) (0) #endif +#if defined(CONFIG_PPC_MERGE) +static struct of_device_id mpc52xx_uart_of_match[] = { + { .type = "serial", .compatible = "mpc52xx-psc-uart", }, + { .type = "serial", .compatible = "mpc5200-psc", }, /* Efika only! */ + {}, +}; +#endif + /* ======================================================================== */ /* UART operations */ @@ -330,7 +369,7 @@ mpc52xx_uart_release_port(struct uart_port *port) port->membase = NULL; } - release_mem_region(port->mapbase, MPC52xx_PSC_SIZE); + release_mem_region(port->mapbase, sizeof(struct mpc52xx_psc)); } static int @@ -339,12 +378,13 @@ mpc52xx_uart_request_port(struct uart_port *port) int err; if (port->flags & UPF_IOREMAP) /* Need to remap ? */ - port->membase = ioremap(port->mapbase, MPC52xx_PSC_SIZE); + port->membase = ioremap(port->mapbase, + sizeof(struct mpc52xx_psc)); if (!port->membase) return -EINVAL; - err = request_mem_region(port->mapbase, MPC52xx_PSC_SIZE, + err = request_mem_region(port->mapbase, sizeof(struct mpc52xx_psc), "mpc52xx_psc_uart") != NULL ? 0 : -EBUSY; if (err && (port->flags & UPF_IOREMAP)) { @@ -561,13 +601,18 @@ mpc52xx_console_get_options(struct uart_port *port, struct mpc52xx_psc __iomem *psc = PSC(port); unsigned char mr1; + pr_debug("mpc52xx_console_get_options(port=%p)\n", port); + /* Read the mode registers */ out_8(&psc->command,MPC52xx_PSC_SEL_MODE_REG_1); mr1 = in_8(&psc->mode); /* CT{U,L}R are write-only ! */ - *baud = __res.bi_baudrate ? - __res.bi_baudrate : CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD; + *baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD; +#if !defined(CONFIG_PPC_MERGE) + if (__res.bi_baudrate) + *baud = __res.bi_baudrate; +#endif /* Parse them */ switch (mr1 & MPC52xx_PSC_MODE_BITS_MASK) { @@ -620,6 +665,7 @@ mpc52xx_console_write(struct console *co, const char *s, unsigned int count) out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); } +#if !defined(CONFIG_PPC_MERGE) static int __init mpc52xx_console_setup(struct console *co, char *options) { @@ -654,6 +700,78 @@ mpc52xx_console_setup(struct console *co, char *options) return uart_set_options(port, co, baud, parity, bits, flow); } +#else + +static int __init +mpc52xx_console_setup(struct console *co, char *options) +{ + struct uart_port *port = &mpc52xx_uart_ports[co->index]; + struct device_node *np = mpc52xx_uart_nodes[co->index]; + unsigned int ipb_freq; + struct resource res; + int ret; + + int baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + pr_debug("mpc52xx_console_setup co=%p, co->index=%i, options=%s\n", + co, co->index, options); + + if ((co->index < 0) || (co->index > MPC52xx_PSC_MAXNUM)) { + pr_debug("PSC%x out of range\n", co->index); + return -EINVAL; + } + + if (!np) { + pr_debug("PSC%x not found in device tree\n", co->index); + return -EINVAL; + } + + pr_debug("Console on ttyPSC%x is %s\n", + co->index, mpc52xx_uart_nodes[co->index]->full_name); + + /* Fetch register locations */ + if ((ret = of_address_to_resource(np, 0, &res)) != 0) { + pr_debug("Could not get resources for PSC%x\n", co->index); + return ret; + } + + /* Search for bus-frequency property in this node or a parent */ + if ((ipb_freq = mpc52xx_find_ipb_freq(np)) == 0) { + pr_debug("Could not find IPB bus frequency!\n"); + return -EINVAL; + } + + /* Basic port init. Needed since we use some uart_??? func before + * real init for early access */ + spin_lock_init(&port->lock); + port->uartclk = ipb_freq / 2; + port->ops = &mpc52xx_uart_ops; + port->mapbase = res.start; + port->membase = ioremap(res.start, sizeof(struct mpc52xx_psc)); + port->irq = irq_of_parse_and_map(np, 0); + + if (port->membase == NULL) + return -EINVAL; + + pr_debug("mpc52xx-psc uart at %lx, mapped to %p, irq=%x, freq=%i\n", + port->mapbase, port->membase, port->irq, port->uartclk); + + /* Setup the port parameters accoding to options */ + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + mpc52xx_console_get_options(port, &baud, &parity, &bits, &flow); + + pr_debug("Setting console parameters: %i %i%c1 flow=%c\n", + baud, bits, parity, flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} +#endif /* defined(CONFIG_PPC_MERGE) */ + static struct uart_driver mpc52xx_uart_driver; @@ -671,6 +789,7 @@ static struct console mpc52xx_console = { static int __init mpc52xx_console_init(void) { + mpc52xx_uart_of_enumerate(); register_console(&mpc52xx_console); return 0; } @@ -698,6 +817,7 @@ static struct uart_driver mpc52xx_uart_driver = { }; +#if !defined(CONFIG_PPC_MERGE) /* ======================================================================== */ /* Platform Driver */ /* ======================================================================== */ @@ -721,8 +841,6 @@ mpc52xx_uart_probe(struct platform_device *dev) /* Init the port structure */ port = &mpc52xx_uart_ports[idx]; - memset(port, 0x00, sizeof(struct uart_port)); - spin_lock_init(&port->lock); port->uartclk = __res.bi_ipbfreq / 2; /* Look at CTLR doc */ port->fifosize = 512; @@ -731,6 +849,7 @@ mpc52xx_uart_probe(struct platform_device *dev) ( uart_console(port) ? 0 : UPF_IOREMAP ); port->line = idx; port->ops = &mpc52xx_uart_ops; + port->dev = &dev->dev; /* Search for IRQ and mapbase */ for (i=0 ; inum_resources ; i++, res++) { @@ -787,6 +906,7 @@ mpc52xx_uart_resume(struct platform_device *dev) } #endif + static struct platform_driver mpc52xx_uart_platform_driver = { .probe = mpc52xx_uart_probe, .remove = mpc52xx_uart_remove, @@ -798,6 +918,184 @@ static struct platform_driver mpc52xx_uart_platform_driver = { .name = "mpc52xx-psc", }, }; +#endif /* !defined(CONFIG_PPC_MERGE) */ + + +#if defined(CONFIG_PPC_MERGE) +/* ======================================================================== */ +/* OF Platform Driver */ +/* ======================================================================== */ + +static int __devinit +mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) +{ + int idx = -1; + unsigned int ipb_freq; + struct uart_port *port = NULL; + struct resource res; + int ret; + + dev_dbg(&op->dev, "mpc52xx_uart_probe(op=%p, match=%p)\n", op, match); + + /* Check validity & presence */ + for (idx = 0; idx < MPC52xx_PSC_MAXNUM; idx++) + if (mpc52xx_uart_nodes[idx] == op->node) + break; + if (idx >= MPC52xx_PSC_MAXNUM) + return -EINVAL; + pr_debug("Found %s assigned to ttyPSC%x\n", + mpc52xx_uart_nodes[idx]->full_name, idx); + + /* Search for bus-frequency property in this node or a parent */ + if ((ipb_freq = mpc52xx_find_ipb_freq(op->node)) == 0) { + dev_dbg(&op->dev, "Could not find IPB bus frequency!\n"); + return -EINVAL; + } + + /* Init the port structure */ + port = &mpc52xx_uart_ports[idx]; + + spin_lock_init(&port->lock); + port->uartclk = ipb_freq / 2; + port->fifosize = 512; + port->iotype = UPIO_MEM; + port->flags = UPF_BOOT_AUTOCONF | + ( uart_console(port) ? 0 : UPF_IOREMAP ); + port->line = idx; + port->ops = &mpc52xx_uart_ops; + port->dev = &op->dev; + + /* Search for IRQ and mapbase */ + if ((ret = of_address_to_resource(op->node, 0, &res)) != 0) + return ret; + + port->mapbase = res.start; + port->irq = irq_of_parse_and_map(op->node, 0); + + dev_dbg(&op->dev, "mpc52xx-psc uart at %lx, irq=%x, freq=%i\n", + port->mapbase, port->irq, port->uartclk); + + if ((port->irq==NO_IRQ) || !port->mapbase) { + printk(KERN_ERR "Could not allocate resources for PSC\n"); + return -EINVAL; + } + + /* Add the port to the uart sub-system */ + ret = uart_add_one_port(&mpc52xx_uart_driver, port); + if (!ret) + dev_set_drvdata(&op->dev, (void*)port); + + return ret; +} + +static int +mpc52xx_uart_of_remove(struct of_device *op) +{ + struct uart_port *port = dev_get_drvdata(&op->dev); + dev_set_drvdata(&op->dev, NULL); + + if (port) + uart_remove_one_port(&mpc52xx_uart_driver, port); + + return 0; +} + +#ifdef CONFIG_PM +static int +mpc52xx_uart_of_suspend(struct of_device *op, pm_message_t state) +{ + struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); + + if (port) + uart_suspend_port(&mpc52xx_uart_driver, port); + + return 0; +} + +static int +mpc52xx_uart_of_resume(struct of_device *op) +{ + struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); + + if (port) + uart_resume_port(&mpc52xx_uart_driver, port); + + return 0; +} +#endif + +static void +mpc52xx_uart_of_assign(struct device_node *np, int idx) +{ + int free_idx = -1; + int i; + + /* Find the first free node */ + for (i = 0; i < MPC52xx_PSC_MAXNUM; i++) { + if (mpc52xx_uart_nodes[i] == NULL) { + free_idx = i; + break; + } + } + + if ((idx < 0) || (idx >= MPC52xx_PSC_MAXNUM)) + idx = free_idx; + + if (idx < 0) + return; /* No free slot; abort */ + + /* If the slot is already occupied, then swap slots */ + if (mpc52xx_uart_nodes[idx] && (free_idx != -1)) + mpc52xx_uart_nodes[free_idx] = mpc52xx_uart_nodes[idx]; + mpc52xx_uart_nodes[i] = np; +} + +static void +mpc52xx_uart_of_enumerate(void) +{ + static int enum_done = 0; + struct device_node *np; + const unsigned int *devno; + int i; + + if (enum_done) + return; + + for_each_node_by_type(np, "serial") { + if (!of_match_node(mpc52xx_uart_of_match, np)) + continue; + + /* Is a particular device number requested? */ + devno = get_property(np, "device_no", NULL); + mpc52xx_uart_of_assign(of_node_get(np), devno ? *devno : -1); + } + + enum_done = 1; + + for (i = 0; i < MPC52xx_PSC_MAXNUM; i++) { + if (mpc52xx_uart_nodes[i]) + pr_debug("%s assigned to ttyPSC%x\n", + mpc52xx_uart_nodes[i]->full_name, i); + } +} + +MODULE_DEVICE_TABLE(of, mpc52xx_uart_of_match); + +static struct of_platform_driver mpc52xx_uart_of_driver = { + .owner = THIS_MODULE, + .name = "mpc52xx-psc-uart", + .match_table = mpc52xx_uart_of_match, + .probe = mpc52xx_uart_of_probe, + .remove = mpc52xx_uart_of_remove, +#ifdef CONFIG_PM + .suspend = mpc52xx_uart_of_suspend, + .resume = mpc52xx_uart_of_resume, +#endif + .driver = { + .name = "mpc52xx-psc-uart", + }, +}; +#endif /* defined(CONFIG_PPC_MERGE) */ /* ======================================================================== */ @@ -809,22 +1107,45 @@ mpc52xx_uart_init(void) { int ret; - printk(KERN_INFO "Serial: MPC52xx PSC driver\n"); + printk(KERN_INFO "Serial: MPC52xx PSC UART driver\n"); - ret = uart_register_driver(&mpc52xx_uart_driver); - if (ret == 0) { - ret = platform_driver_register(&mpc52xx_uart_platform_driver); - if (ret) - uart_unregister_driver(&mpc52xx_uart_driver); + if ((ret = uart_register_driver(&mpc52xx_uart_driver)) != 0) { + printk(KERN_ERR "%s: uart_register_driver failed (%i)\n", + __FILE__, ret); + return ret; } - return ret; +#if defined(CONFIG_PPC_MERGE) + mpc52xx_uart_of_enumerate(); + + ret = of_register_platform_driver(&mpc52xx_uart_of_driver); + if (ret) { + printk(KERN_ERR "%s: of_register_platform_driver failed (%i)\n", + __FILE__, ret); + uart_unregister_driver(&mpc52xx_uart_driver); + return ret; + } +#else + ret = platform_driver_register(&mpc52xx_uart_platform_driver); + if (ret) { + printk(KERN_ERR "%s: platform_driver_register failed (%i)\n", + __FILE__, ret); + uart_unregister_driver(&mpc52xx_uart_driver); + return ret; + } +#endif + + return 0; } static void __exit mpc52xx_uart_exit(void) { +#if defined(CONFIG_PPC_MERGE) + of_unregister_platform_driver(&mpc52xx_uart_of_driver); +#else platform_driver_unregister(&mpc52xx_uart_platform_driver); +#endif uart_unregister_driver(&mpc52xx_uart_driver); } -- cgit v1.2.3