From a5dae76a3d8012b0ce0ff04dfe6101339df49740 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Fri, 5 Dec 2008 23:10:26 +0300 Subject: powerpc: Implement get_brgfreq() and get_baudrate() stubs This is needed to not bother with ugly #ifdefs in the drivers. Signed-off-by: Anton Vorontsov Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_soc.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/powerpc/sysdev/fsl_soc.h b/arch/powerpc/sysdev/fsl_soc.h index 60f7f227327c..9c744e4285a0 100644 --- a/arch/powerpc/sysdev/fsl_soc.h +++ b/arch/powerpc/sysdev/fsl_soc.h @@ -5,8 +5,13 @@ #include extern phys_addr_t get_immrbase(void); +#if defined(CONFIG_CPM2) || defined(CONFIG_QUICC_ENGINE) || defined(CONFIG_8xx) extern u32 get_brgfreq(void); extern u32 get_baudrate(void); +#else +static inline u32 get_brgfreq(void) { return -1; } +static inline u32 get_baudrate(void) { return -1; } +#endif extern u32 fsl_get_sys_freq(void); struct spi_board_info; -- cgit v1.2.3 From 81b36a0b6ea97c5c2f59e504c56e5a57ee26884a Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Fri, 5 Dec 2008 18:48:07 +0300 Subject: powerpc/83xx: Fix sparse warnings in board files This patch fixes following sparse warnings: CHECK 83xx/usb.c 83xx/usb.c:205:5: warning: symbol 'mpc837x_usb_cfg' was not declared. Should it be static? CHECK 83xx/mpc831x_rdb.c 83xx/mpc831x_rdb.c:45:13: warning: symbol 'mpc831x_rdb_init_IRQ' was not declared. Should it be static? CHECK 83xx/mpc832x_rdb.c 83xx/mpc832x_rdb.c:133:13: warning: symbol 'mpc832x_rdb_init_IRQ' was not declared. Should it be static? CHECK 83xx/mpc832x_mds.c 83xx/mpc832x_mds.c:68:12: warning: Using plain integer as NULL pointer 83xx/mpc832x_mds.c:72:13: warning: incorrect type in assignment (different address spaces) 83xx/mpc832x_mds.c:72:13: expected unsigned char [usertype] *static [toplevel] bcsr_regs 83xx/mpc832x_mds.c:72:13: got void [noderef] * 83xx/mpc832x_mds.c:99:11: warning: incorrect type in argument 1 (different address spaces) 83xx/mpc832x_mds.c:99:11: expected void volatile [noderef] *addr 83xx/mpc832x_mds.c:99:11: got unsigned char [usertype] *static [toplevel] bcsr_regs Signed-off-by: Anton Vorontsov Signed-off-by: Kumar Gala --- arch/powerpc/platforms/83xx/mpc831x_rdb.c | 2 +- arch/powerpc/platforms/83xx/mpc832x_mds.c | 9 ++++----- arch/powerpc/platforms/83xx/mpc832x_rdb.c | 2 +- arch/powerpc/platforms/83xx/mpc837x_mds.c | 1 - arch/powerpc/platforms/83xx/mpc837x_rdb.c | 2 -- arch/powerpc/platforms/83xx/mpc83xx.h | 1 + 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/arch/powerpc/platforms/83xx/mpc831x_rdb.c b/arch/powerpc/platforms/83xx/mpc831x_rdb.c index a428f8d1ac80..5177bdd2c62a 100644 --- a/arch/powerpc/platforms/83xx/mpc831x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc831x_rdb.c @@ -42,7 +42,7 @@ static void __init mpc831x_rdb_setup_arch(void) mpc831x_usb_cfg(); } -void __init mpc831x_rdb_init_IRQ(void) +static void __init mpc831x_rdb_init_IRQ(void) { struct device_node *np; diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c index ec43477caa63..ec0b401bc9cf 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c @@ -49,8 +49,6 @@ #define DBG(fmt...) #endif -static u8 *bcsr_regs = NULL; - /* ************************************************************************ * * Setup the architecture @@ -59,13 +57,14 @@ static u8 *bcsr_regs = NULL; static void __init mpc832x_sys_setup_arch(void) { struct device_node *np; + u8 __iomem *bcsr_regs = NULL; if (ppc_md.progress) ppc_md.progress("mpc832x_sys_setup_arch()", 0); /* Map BCSR area */ np = of_find_node_by_name(NULL, "bcsr"); - if (np != 0) { + if (np) { struct resource res; of_address_to_resource(np, 0, &res); @@ -93,9 +92,9 @@ static void __init mpc832x_sys_setup_arch(void) != NULL){ /* Reset the Ethernet PHYs */ #define BCSR8_FETH_RST 0x50 - bcsr_regs[8] &= ~BCSR8_FETH_RST; + clrbits8(&bcsr_regs[8], BCSR8_FETH_RST); udelay(1000); - bcsr_regs[8] |= BCSR8_FETH_RST; + setbits8(&bcsr_regs[8], BCSR8_FETH_RST); iounmap(bcsr_regs); of_node_put(np); } diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c index 0300268ce5b8..d8eb4473a1bc 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -130,7 +130,7 @@ static int __init mpc832x_declare_of_platform_devices(void) } machine_device_initcall(mpc832x_rdb, mpc832x_declare_of_platform_devices); -void __init mpc832x_rdb_init_IRQ(void) +static void __init mpc832x_rdb_init_IRQ(void) { struct device_node *np; diff --git a/arch/powerpc/platforms/83xx/mpc837x_mds.c b/arch/powerpc/platforms/83xx/mpc837x_mds.c index 8bb13c807142..530ef990ca7c 100644 --- a/arch/powerpc/platforms/83xx/mpc837x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc837x_mds.c @@ -26,7 +26,6 @@ #define BCSR12_USB_SER_MASK 0x8a #define BCSR12_USB_SER_PIN 0x80 #define BCSR12_USB_SER_DEVICE 0x02 -extern int mpc837x_usb_cfg(void); static int mpc837xmds_usb_cfg(void) { diff --git a/arch/powerpc/platforms/83xx/mpc837x_rdb.c b/arch/powerpc/platforms/83xx/mpc837x_rdb.c index da030afa2e2c..1d096545322b 100644 --- a/arch/powerpc/platforms/83xx/mpc837x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc837x_rdb.c @@ -21,8 +21,6 @@ #include "mpc83xx.h" -extern int mpc837x_usb_cfg(void); - /* ************************************************************************ * * Setup the architecture diff --git a/arch/powerpc/platforms/83xx/mpc83xx.h b/arch/powerpc/platforms/83xx/mpc83xx.h index 2a7cbabb410a..83cfe51526ec 100644 --- a/arch/powerpc/platforms/83xx/mpc83xx.h +++ b/arch/powerpc/platforms/83xx/mpc83xx.h @@ -61,6 +61,7 @@ extern void mpc83xx_restart(char *cmd); extern long mpc83xx_time_init(void); +extern int mpc837x_usb_cfg(void); extern int mpc834x_usb_cfg(void); extern int mpc831x_usb_cfg(void); -- cgit v1.2.3 From 78c7705037ed9f107660178e17aa73f8bc4127e8 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Wed, 3 Dec 2008 22:27:52 +0300 Subject: powerpc/83xx: Fix sparse warnings in mpc836x_mds.c This patch fixes following sparse warnings: CHECK mpc836x_mds.c mpc836x_mds.c:75:12: warning: Using plain integer as NULL pointer mpc836x_mds.c:79:13: warning: incorrect type in assignment (different address spaces) mpc836x_mds.c:79:13: expected unsigned char [usertype] *static [toplevel] bcsr_regs mpc836x_mds.c:79:13: got void [noderef] * mpc836x_mds.c:105:3: warning: incorrect type in argument 1 (different address spaces) mpc836x_mds.c:105:3: expected unsigned char volatile [noderef] [usertype] *addr mpc836x_mds.c:105:3: got unsigned char [usertype] * mpc836x_mds.c:105:3: warning: incorrect type in argument 1 (different address spaces) mpc836x_mds.c:105:3: expected unsigned char const volatile [noderef] [usertype] *addr mpc836x_mds.c:105:3: got unsigned char [usertype] * mpc836x_mds.c:107:3: warning: incorrect type in argument 1 (different address spaces) mpc836x_mds.c:107:3: expected unsigned char volatile [noderef] [usertype] *addr mpc836x_mds.c:107:3: got unsigned char [usertype] * mpc836x_mds.c:107:3: warning: incorrect type in argument 1 (different address spaces) mpc836x_mds.c:107:3: expected unsigned char const volatile [noderef] [usertype] *addr mpc836x_mds.c:107:3: got unsigned char [usertype] * mpc836x_mds.c:131:11: warning: incorrect type in argument 1 (different address spaces) mpc836x_mds.c:131:11: expected void volatile [noderef] *addr mpc836x_mds.c:131:11: got unsigned char [usertype] *static [toplevel] bcsr_regs Signed-off-by: Anton Vorontsov Signed-off-by: Kumar Gala --- arch/powerpc/platforms/83xx/mpc836x_mds.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c index 9d46e5bdd101..c0a09c34956b 100644 --- a/arch/powerpc/platforms/83xx/mpc836x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -55,8 +56,6 @@ #define DBG(fmt...) #endif -static u8 *bcsr_regs = NULL; - /* ************************************************************************ * * Setup the architecture @@ -65,13 +64,14 @@ static u8 *bcsr_regs = NULL; static void __init mpc836x_mds_setup_arch(void) { struct device_node *np; + u8 __iomem *bcsr_regs = NULL; if (ppc_md.progress) ppc_md.progress("mpc836x_mds_setup_arch()", 0); /* Map BCSR area */ np = of_find_node_by_name(NULL, "bcsr"); - if (np != 0) { + if (np) { struct resource res; of_address_to_resource(np, 0, &res); -- cgit v1.2.3 From 1b9e89046c31fd39d08742915b6bd72f6c239608 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Wed, 3 Dec 2008 22:27:38 +0300 Subject: powerpc/qe: Implement QE Pin Multiplexing API With this API we're able to set a QE pin to the GPIO mode or a dedicated peripheral function. The API relies on the fact that QE gpio controllers are registered. If they aren't, the API won't work (gracefully though). There is one caveat though: if anybody occupied the node->data before us, or overwrote it, then bad things will happen. Luckily this is all in the platform code that we fully control, so this should never happen. I could implement more checks (for example we could create a list of successfully registered QE controllers, and compare the node->data in the qe_pin_request()), but this is unneeded if nobody is going to do silly things behind our back. Signed-off-by: Anton Vorontsov Signed-off-by: Kumar Gala --- arch/powerpc/include/asm/qe.h | 21 ++++ arch/powerpc/sysdev/qe_lib/gpio.c | 195 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) diff --git a/arch/powerpc/include/asm/qe.h b/arch/powerpc/include/asm/qe.h index edee15d269ea..32274407b93a 100644 --- a/arch/powerpc/include/asm/qe.h +++ b/arch/powerpc/include/asm/qe.h @@ -17,6 +17,8 @@ #ifdef __KERNEL__ #include +#include +#include #include #include @@ -112,6 +114,25 @@ extern int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain, int assignment, int has_irq); extern int par_io_data_set(u8 port, u8 pin, u8 val); +/* + * Pin multiplexing functions. + */ +struct qe_pin; +#ifdef CONFIG_QE_GPIO +extern struct qe_pin *qe_pin_request(struct device_node *np, int index); +extern void qe_pin_free(struct qe_pin *qe_pin); +extern void qe_pin_set_gpio(struct qe_pin *qe_pin); +extern void qe_pin_set_dedicated(struct qe_pin *pin); +#else +static inline struct qe_pin *qe_pin_request(struct device_node *np, int index) +{ + return ERR_PTR(-ENOSYS); +} +static inline void qe_pin_free(struct qe_pin *qe_pin) {} +static inline void qe_pin_set_gpio(struct qe_pin *qe_pin) {} +static inline void qe_pin_set_dedicated(struct qe_pin *pin) {} +#endif /* CONFIG_QE_GPIO */ + /* QE internal API */ int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input); enum qe_clock qe_clock_source(const char *source); diff --git a/arch/powerpc/sysdev/qe_lib/gpio.c b/arch/powerpc/sysdev/qe_lib/gpio.c index 8e5a0bc36d0b..3485288dce31 100644 --- a/arch/powerpc/sysdev/qe_lib/gpio.c +++ b/arch/powerpc/sysdev/qe_lib/gpio.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -24,8 +25,14 @@ struct qe_gpio_chip { struct of_mm_gpio_chip mm_gc; spinlock_t lock; + unsigned long pin_flags[QE_PIO_PINS]; +#define QE_PIN_REQUESTED 0 + /* shadowed data register to clear/set bits safely */ u32 cpdata; + + /* saved_regs used to restore dedicated functions */ + struct qe_pio_regs saved_regs; }; static inline struct qe_gpio_chip * @@ -40,6 +47,12 @@ static void qe_gpio_save_regs(struct of_mm_gpio_chip *mm_gc) struct qe_pio_regs __iomem *regs = mm_gc->regs; qe_gc->cpdata = in_be32(®s->cpdata); + qe_gc->saved_regs.cpdata = qe_gc->cpdata; + qe_gc->saved_regs.cpdir1 = in_be32(®s->cpdir1); + qe_gc->saved_regs.cpdir2 = in_be32(®s->cpdir2); + qe_gc->saved_regs.cppar1 = in_be32(®s->cppar1); + qe_gc->saved_regs.cppar2 = in_be32(®s->cppar2); + qe_gc->saved_regs.cpodr = in_be32(®s->cpodr); } static int qe_gpio_get(struct gpio_chip *gc, unsigned int gpio) @@ -103,6 +116,188 @@ static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) return 0; } +struct qe_pin { + /* + * The qe_gpio_chip name is unfortunate, we should change that to + * something like qe_pio_controller. Someday. + */ + struct qe_gpio_chip *controller; + int num; +}; + +/** + * qe_pin_request - Request a QE pin + * @np: device node to get a pin from + * @index: index of a pin in the device tree + * Context: non-atomic + * + * This function return qe_pin so that you could use it with the rest of + * the QE Pin Multiplexing API. + */ +struct qe_pin *qe_pin_request(struct device_node *np, int index) +{ + struct qe_pin *qe_pin; + struct device_node *gc; + struct of_gpio_chip *of_gc = NULL; + struct of_mm_gpio_chip *mm_gc; + struct qe_gpio_chip *qe_gc; + int err; + int size; + const void *gpio_spec; + const u32 *gpio_cells; + unsigned long flags; + + qe_pin = kzalloc(sizeof(*qe_pin), GFP_KERNEL); + if (!qe_pin) { + pr_debug("%s: can't allocate memory\n", __func__); + return ERR_PTR(-ENOMEM); + } + + err = of_parse_phandles_with_args(np, "gpios", "#gpio-cells", index, + &gc, &gpio_spec); + if (err) { + pr_debug("%s: can't parse gpios property\n", __func__); + goto err0; + } + + if (!of_device_is_compatible(gc, "fsl,mpc8323-qe-pario-bank")) { + pr_debug("%s: tried to get a non-qe pin\n", __func__); + err = -EINVAL; + goto err1; + } + + of_gc = gc->data; + if (!of_gc) { + pr_debug("%s: gpio controller %s isn't registered\n", + np->full_name, gc->full_name); + err = -ENODEV; + goto err1; + } + + gpio_cells = of_get_property(gc, "#gpio-cells", &size); + if (!gpio_cells || size != sizeof(*gpio_cells) || + *gpio_cells != of_gc->gpio_cells) { + pr_debug("%s: wrong #gpio-cells for %s\n", + np->full_name, gc->full_name); + err = -EINVAL; + goto err1; + } + + err = of_gc->xlate(of_gc, np, gpio_spec, NULL); + if (err < 0) + goto err1; + + mm_gc = to_of_mm_gpio_chip(&of_gc->gc); + qe_gc = to_qe_gpio_chip(mm_gc); + + spin_lock_irqsave(&qe_gc->lock, flags); + + if (test_and_set_bit(QE_PIN_REQUESTED, &qe_gc->pin_flags[err]) == 0) { + qe_pin->controller = qe_gc; + qe_pin->num = err; + err = 0; + } else { + err = -EBUSY; + } + + spin_unlock_irqrestore(&qe_gc->lock, flags); + + if (!err) + return qe_pin; +err1: + of_node_put(gc); +err0: + kfree(qe_pin); + pr_debug("%s failed with status %d\n", __func__, err); + return ERR_PTR(err); +} +EXPORT_SYMBOL(qe_pin_request); + +/** + * qe_pin_free - Free a pin + * @qe_pin: pointer to the qe_pin structure + * Context: any + * + * This function frees the qe_pin structure and makes a pin available + * for further qe_pin_request() calls. + */ +void qe_pin_free(struct qe_pin *qe_pin) +{ + struct qe_gpio_chip *qe_gc = qe_pin->controller; + unsigned long flags; + const int pin = qe_pin->num; + + spin_lock_irqsave(&qe_gc->lock, flags); + test_and_clear_bit(QE_PIN_REQUESTED, &qe_gc->pin_flags[pin]); + spin_unlock_irqrestore(&qe_gc->lock, flags); + + kfree(qe_pin); +} +EXPORT_SYMBOL(qe_pin_free); + +/** + * qe_pin_set_dedicated - Revert a pin to a dedicated peripheral function mode + * @qe_pin: pointer to the qe_pin structure + * Context: any + * + * This function resets a pin to a dedicated peripheral function that + * has been set up by the firmware. + */ +void qe_pin_set_dedicated(struct qe_pin *qe_pin) +{ + struct qe_gpio_chip *qe_gc = qe_pin->controller; + struct qe_pio_regs __iomem *regs = qe_gc->mm_gc.regs; + struct qe_pio_regs *sregs = &qe_gc->saved_regs; + int pin = qe_pin->num; + u32 mask1 = 1 << (QE_PIO_PINS - (pin + 1)); + u32 mask2 = 0x3 << (QE_PIO_PINS - (pin % (QE_PIO_PINS / 2) + 1) * 2); + bool second_reg = pin > (QE_PIO_PINS / 2) - 1; + unsigned long flags; + + spin_lock_irqsave(&qe_gc->lock, flags); + + if (second_reg) { + clrsetbits_be32(®s->cpdir2, mask2, sregs->cpdir2 & mask2); + clrsetbits_be32(®s->cppar2, mask2, sregs->cppar2 & mask2); + } else { + clrsetbits_be32(®s->cpdir1, mask2, sregs->cpdir1 & mask2); + clrsetbits_be32(®s->cppar1, mask2, sregs->cppar1 & mask2); + } + + if (sregs->cpdata & mask1) + qe_gc->cpdata |= mask1; + else + qe_gc->cpdata &= ~mask1; + + out_be32(®s->cpdata, qe_gc->cpdata); + clrsetbits_be32(®s->cpodr, mask1, sregs->cpodr & mask1); + + spin_unlock_irqrestore(&qe_gc->lock, flags); +} +EXPORT_SYMBOL(qe_pin_set_dedicated); + +/** + * qe_pin_set_gpio - Set a pin to the GPIO mode + * @qe_pin: pointer to the qe_pin structure + * Context: any + * + * This function sets a pin to the GPIO mode. + */ +void qe_pin_set_gpio(struct qe_pin *qe_pin) +{ + struct qe_gpio_chip *qe_gc = qe_pin->controller; + struct qe_pio_regs __iomem *regs = qe_gc->mm_gc.regs; + unsigned long flags; + + spin_lock_irqsave(&qe_gc->lock, flags); + + /* Let's make it input by default, GPIO API is able to change that. */ + __par_io_config_pin(regs, qe_pin->num, QE_PIO_DIR_IN, 0, 0, 0); + + spin_unlock_irqrestore(&qe_gc->lock, flags); +} +EXPORT_SYMBOL(qe_pin_set_gpio); + static int __init qe_add_gpiochips(void) { struct device_node *np; -- cgit v1.2.3 From 94409d6e1088517b6d6c8e669c604cc86d08ac1b Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 18 Dec 2008 19:37:23 +0300 Subject: powerpc: Add device tree bindings for BCSR GPIO banks The patch adds bindings for BCSR GPIO banks, the bindings are used to describe particular BCSR registers that act as simple GPIO controllers. These GPIO banks might control power switches, SPI chip-selects, LEDs, etc. While at it, also fix "length" spelling error in the PIXIS FPGA bindings. Signed-off-by: Anton Vorontsov Signed-off-by: Kumar Gala --- Documentation/powerpc/dts-bindings/fsl/board.txt | 32 +++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/Documentation/powerpc/dts-bindings/fsl/board.txt b/Documentation/powerpc/dts-bindings/fsl/board.txt index 81a917ef96e9..6c974d28eeb4 100644 --- a/Documentation/powerpc/dts-bindings/fsl/board.txt +++ b/Documentation/powerpc/dts-bindings/fsl/board.txt @@ -18,7 +18,7 @@ This is the memory-mapped registers for on board FPGA. Required properities: - compatible : should be "fsl,fpga-pixis". -- reg : should contain the address and the lenght of the FPPGA register +- reg : should contain the address and the length of the FPPGA register set. Example (MPC8610HPCD): @@ -27,3 +27,33 @@ Example (MPC8610HPCD): compatible = "fsl,fpga-pixis"; reg = <0xe8000000 32>; }; + +* Freescale BCSR GPIO banks + +Some BCSR registers act as simple GPIO controllers, each such +register can be represented by the gpio-controller node. + +Required properities: +- compatible : Should be "fsl,-bcsr-gpio". +- reg : Should contain the address and the length of the GPIO bank + register. +- #gpio-cells : Should be two. The first cell is the pin number and the + second cell is used to specify optional paramters (currently unused). +- gpio-controller : Marks the port as GPIO controller. + +Example: + + bcsr@1,0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "fsl,mpc8360mds-bcsr"; + reg = <1 0 0x8000>; + ranges = <0 1 0 0x8000>; + + bcsr13: gpio-controller@d { + #gpio-cells = <2>; + compatible = "fsl,mpc8360mds-bcsr-gpio"; + reg = <0xd 1>; + gpio-controller; + }; + }; -- cgit v1.2.3 From 3d64de9c50619d32eb71d993d23a50b98d12d3c0 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 18 Dec 2008 19:37:26 +0300 Subject: powerpc: Implement GPIO driver for simple memory-mapped banks The driver supports very simple GPIO controllers, that is, when a controller provides just a 'data' register. Such controllers may be found in various BCSRs (Board's FPGAs used to control board's switches, LEDs, chip-selects, Ethernet/USB PHY power, etc). So far we support only 1-byte GPIO banks. Support for other widths may be implemented when/if needed. p.s. To avoid "made up" compatible entries (like compatible = "simple-gpio"), boards must call simple_gpiochip_init() to pass the compatible string. Signed-off-by: Anton Vorontsov Signed-off-by: Kumar Gala --- arch/powerpc/platforms/Kconfig | 11 +++ arch/powerpc/sysdev/Makefile | 1 + arch/powerpc/sysdev/simple_gpio.c | 155 ++++++++++++++++++++++++++++++++++++++ arch/powerpc/sysdev/simple_gpio.h | 12 +++ 4 files changed, 179 insertions(+) create mode 100644 arch/powerpc/sysdev/simple_gpio.c create mode 100644 arch/powerpc/sysdev/simple_gpio.h diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 47e956c871fe..47fe2bea9865 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -312,4 +312,15 @@ config MPC8xxx_GPIO Say Y here if you're going to use hardware that connects to the MPC831x/834x/837x/8572/8610 GPIOs. +config SIMPLE_GPIO + bool "Support for simple, memory-mapped GPIO controllers" + depends on PPC + select GENERIC_GPIO + select ARCH_REQUIRE_GPIOLIB + help + Say Y here to support simple, memory-mapped GPIO controllers. + These are usually BCSRs used to control board's switches, LEDs, + chip-selects, Ethernet/USB PHY's power and various other small + on-board peripherals. + endmenu diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index 5afce115ab1f..b33b28a6fe12 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y) obj-$(CONFIG_FSL_LBC) += fsl_lbc.o obj-$(CONFIG_FSL_GTM) += fsl_gtm.o obj-$(CONFIG_MPC8xxx_GPIO) += mpc8xxx_gpio.o +obj-$(CONFIG_SIMPLE_GPIO) += simple_gpio.o obj-$(CONFIG_RAPIDIO) += fsl_rio.o obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ diff --git a/arch/powerpc/sysdev/simple_gpio.c b/arch/powerpc/sysdev/simple_gpio.c new file mode 100644 index 000000000000..43c4569e24b7 --- /dev/null +++ b/arch/powerpc/sysdev/simple_gpio.c @@ -0,0 +1,155 @@ +/* + * Simple Memory-Mapped GPIOs + * + * Copyright (c) MontaVista Software, Inc. 2008. + * + * Author: Anton Vorontsov + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "simple_gpio.h" + +struct u8_gpio_chip { + struct of_mm_gpio_chip mm_gc; + spinlock_t lock; + + /* shadowed data register to clear/set bits safely */ + u8 data; +}; + +static struct u8_gpio_chip *to_u8_gpio_chip(struct of_mm_gpio_chip *mm_gc) +{ + return container_of(mm_gc, struct u8_gpio_chip, mm_gc); +} + +static u8 u8_pin2mask(unsigned int pin) +{ + return 1 << (8 - 1 - pin); +} + +static int u8_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + + return in_8(mm_gc->regs) & u8_pin2mask(gpio); +} + +static void u8_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct u8_gpio_chip *u8_gc = to_u8_gpio_chip(mm_gc); + unsigned long flags; + + spin_lock_irqsave(&u8_gc->lock, flags); + + if (val) + u8_gc->data |= u8_pin2mask(gpio); + else + u8_gc->data &= ~u8_pin2mask(gpio); + + out_8(mm_gc->regs, u8_gc->data); + + spin_unlock_irqrestore(&u8_gc->lock, flags); +} + +static int u8_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + return 0; +} + +static int u8_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + u8_gpio_set(gc, gpio, val); + return 0; +} + +static void u8_gpio_save_regs(struct of_mm_gpio_chip *mm_gc) +{ + struct u8_gpio_chip *u8_gc = to_u8_gpio_chip(mm_gc); + + u8_gc->data = in_8(mm_gc->regs); +} + +static int __init u8_simple_gpiochip_add(struct device_node *np) +{ + int ret; + struct u8_gpio_chip *u8_gc; + struct of_mm_gpio_chip *mm_gc; + struct of_gpio_chip *of_gc; + struct gpio_chip *gc; + + u8_gc = kzalloc(sizeof(*u8_gc), GFP_KERNEL); + if (!u8_gc) + return -ENOMEM; + + spin_lock_init(&u8_gc->lock); + + mm_gc = &u8_gc->mm_gc; + of_gc = &mm_gc->of_gc; + gc = &of_gc->gc; + + mm_gc->save_regs = u8_gpio_save_regs; + of_gc->gpio_cells = 2; + gc->ngpio = 8; + gc->direction_input = u8_gpio_dir_in; + gc->direction_output = u8_gpio_dir_out; + gc->get = u8_gpio_get; + gc->set = u8_gpio_set; + + ret = of_mm_gpiochip_add(np, mm_gc); + if (ret) + goto err; + return 0; +err: + kfree(u8_gc); + return ret; +} + +void __init simple_gpiochip_init(const char *compatible) +{ + struct device_node *np; + + for_each_compatible_node(np, NULL, compatible) { + int ret; + struct resource r; + + ret = of_address_to_resource(np, 0, &r); + if (ret) + goto err; + + switch (resource_size(&r)) { + case 1: + ret = u8_simple_gpiochip_add(np); + if (ret) + goto err; + break; + default: + /* + * Whenever you need support for GPIO bank width > 1, + * please just turn u8_ code into huge macros, and + * construct needed uX_ code with it. + */ + ret = -ENOSYS; + goto err; + } + continue; +err: + pr_err("%s: registration failed, status %d\n", + np->full_name, ret); + } +} diff --git a/arch/powerpc/sysdev/simple_gpio.h b/arch/powerpc/sysdev/simple_gpio.h new file mode 100644 index 000000000000..3a7b0c513c76 --- /dev/null +++ b/arch/powerpc/sysdev/simple_gpio.h @@ -0,0 +1,12 @@ +#ifndef __SYSDEV_SIMPLE_GPIO_H +#define __SYSDEV_SIMPLE_GPIO_H + +#include + +#ifdef CONFIG_SIMPLE_GPIO +extern void simple_gpiochip_init(const char *compatible); +#else +static inline void simple_gpiochip_init(const char *compatible) {} +#endif /* CONFIG_SIMPLE_GPIO */ + +#endif /* __SYSDEV_SIMPLE_GPIO_H */ -- cgit v1.2.3 From c9c5e52d44b3e68effcda4bef4ff23c6be0c001c Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 18 Dec 2008 19:37:31 +0300 Subject: powerpc/83xx: Add USB Host/Gadget support for MPC8360E-MDS boards - Update the device tree per QE USB bindings; - Add timer (FSL GTM) node; - Add gpio-controller node for BCSR13 bank (GPIOs on that bank are used to control the USB transceiver); - Set up other BCSR registers; - Configure the QE Par IO. The work is loosely based on Li Yang's patch[1], which was used to support peripheral mode only. [1] http://ozlabs.org/pipermail/linuxppc-dev/2008-August/061357.html The s-o-b line of the original patch preserved here. Signed-off-by: Li Yang Signed-off-by: Anton Vorontsov Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/mpc836x_mds.dts | 43 +++++++++++++++++- arch/powerpc/platforms/83xx/mpc836x_mds.c | 75 +++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/boot/dts/mpc836x_mds.dts b/arch/powerpc/boot/dts/mpc836x_mds.dts index 14534d04e4db..6e34f170fa62 100644 --- a/arch/powerpc/boot/dts/mpc836x_mds.dts +++ b/arch/powerpc/boot/dts/mpc836x_mds.dts @@ -69,8 +69,18 @@ }; bcsr@1,0 { + #address-cells = <1>; + #size-cells = <1>; compatible = "fsl,mpc8360mds-bcsr"; reg = <1 0 0x8000>; + ranges = <0 1 0 0x8000>; + + bcsr13: gpio-controller@d { + #gpio-cells = <2>; + compatible = "fsl,mpc8360mds-bcsr-gpio"; + reg = <0xd 1>; + gpio-controller; + }; }; }; @@ -195,10 +205,21 @@ }; par_io@1400 { + #address-cells = <1>; + #size-cells = <1>; reg = <0x1400 0x100>; + ranges = <0 0x1400 0x100>; device_type = "par_io"; num-ports = <7>; + qe_pio_b: gpio-controller@18 { + #gpio-cells = <2>; + compatible = "fsl,mpc8360-qe-pario-bank", + "fsl,mpc8323-qe-pario-bank"; + reg = <0x18 0x18>; + gpio-controller; + }; + pio1: ucc_pin@01 { pio-map = < /* port pin dir open_drain assignment has_irq */ @@ -282,6 +303,15 @@ }; }; + timer@440 { + compatible = "fsl,mpc8360-qe-gtm", + "fsl,qe-gtm", "fsl,gtm"; + reg = <0x440 0x40>; + clock-frequency = <132000000>; + interrupts = <12 13 14 15>; + interrupt-parent = <&qeic>; + }; + spi@4c0 { cell-index = <0>; compatible = "fsl,spi"; @@ -301,11 +331,20 @@ }; usb@6c0 { - compatible = "qe_udc"; + compatible = "fsl,mpc8360-qe-usb", + "fsl,mpc8323-qe-usb"; reg = <0x6c0 0x40 0x8b00 0x100>; interrupts = <11>; interrupt-parent = <&qeic>; - mode = "slave"; + fsl,fullspeed-clock = "clk21"; + fsl,lowspeed-clock = "brg9"; + gpios = <&qe_pio_b 2 0 /* USBOE */ + &qe_pio_b 3 0 /* USBTP */ + &qe_pio_b 8 0 /* USBTN */ + &qe_pio_b 9 0 /* USBRP */ + &qe_pio_b 11 0 /* USBRN */ + &bcsr13 5 0 /* SPEED */ + &bcsr13 4 1>; /* POWER */ }; enet0: ucc@2000 { diff --git a/arch/powerpc/platforms/83xx/mpc836x_mds.c b/arch/powerpc/platforms/83xx/mpc836x_mds.c index c0a09c34956b..09e9d6fb7411 100644 --- a/arch/powerpc/platforms/83xx/mpc836x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc836x_mds.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -93,6 +94,16 @@ static void __init mpc836x_mds_setup_arch(void) for (np = NULL; (np = of_find_node_by_name(np, "ucc")) != NULL;) par_io_of_config(np); +#ifdef CONFIG_QE_USB + /* Must fixup Par IO before QE GPIO chips are registered. */ + par_io_config_pin(1, 2, 1, 0, 3, 0); /* USBOE */ + par_io_config_pin(1, 3, 1, 0, 3, 0); /* USBTP */ + par_io_config_pin(1, 8, 1, 0, 1, 0); /* USBTN */ + par_io_config_pin(1, 10, 2, 0, 3, 0); /* USBRXD */ + par_io_config_pin(1, 9, 2, 1, 3, 0); /* USBRP */ + par_io_config_pin(1, 11, 2, 1, 3, 0); /* USBRN */ + par_io_config_pin(2, 20, 2, 0, 1, 0); /* CLK21 */ +#endif /* CONFIG_QE_USB */ } if ((np = of_find_compatible_node(NULL, "network", "ucc_geth")) @@ -151,6 +162,70 @@ static int __init mpc836x_declare_of_platform_devices(void) } machine_device_initcall(mpc836x_mds, mpc836x_declare_of_platform_devices); +#ifdef CONFIG_QE_USB +static int __init mpc836x_usb_cfg(void) +{ + u8 __iomem *bcsr; + struct device_node *np; + const char *mode; + int ret = 0; + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc8360mds-bcsr"); + if (!np) + return -ENODEV; + + bcsr = of_iomap(np, 0); + of_node_put(np); + if (!bcsr) + return -ENOMEM; + + np = of_find_compatible_node(NULL, NULL, "fsl,mpc8323-qe-usb"); + if (!np) { + ret = -ENODEV; + goto err; + } + +#define BCSR8_TSEC1M_MASK (0x3 << 6) +#define BCSR8_TSEC1M_RGMII (0x0 << 6) +#define BCSR8_TSEC2M_MASK (0x3 << 4) +#define BCSR8_TSEC2M_RGMII (0x0 << 4) + /* + * Default is GMII (2), but we should set it to RGMII (0) if we use + * USB (Eth PHY is in RGMII mode anyway). + */ + clrsetbits_8(&bcsr[8], BCSR8_TSEC1M_MASK | BCSR8_TSEC2M_MASK, + BCSR8_TSEC1M_RGMII | BCSR8_TSEC2M_RGMII); + +#define BCSR13_USBMASK 0x0f +#define BCSR13_nUSBEN 0x08 /* 1 - Disable, 0 - Enable */ +#define BCSR13_USBSPEED 0x04 /* 1 - Full, 0 - Low */ +#define BCSR13_USBMODE 0x02 /* 1 - Host, 0 - Function */ +#define BCSR13_nUSBVCC 0x01 /* 1 - gets VBUS, 0 - supplies VBUS */ + + clrsetbits_8(&bcsr[13], BCSR13_USBMASK, BCSR13_USBSPEED); + + mode = of_get_property(np, "mode", NULL); + if (mode && !strcmp(mode, "peripheral")) { + setbits8(&bcsr[13], BCSR13_nUSBVCC); + qe_usb_clock_set(QE_CLK21, 48000000); + } else { + setbits8(&bcsr[13], BCSR13_USBMODE); + /* + * The BCSR GPIOs are used to control power and + * speed of the USB transceiver. This is needed for + * the USB Host only. + */ + simple_gpiochip_init("fsl,mpc8360mds-bcsr-gpio"); + } + + of_node_put(np); +err: + iounmap(bcsr); + return ret; +} +machine_arch_initcall(mpc836x_mds, mpc836x_usb_cfg); +#endif /* CONFIG_QE_USB */ + static void __init mpc836x_mds_init_IRQ(void) { struct device_node *np; -- cgit v1.2.3 From 25adde18e6deefa3ed2f84738e1b86f474a539b9 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 18 Dec 2008 19:37:34 +0300 Subject: powerpc/83xx: Add USB Host support for MPC8360E-RDK boards Simply add the usb node to support USB host on the MPC8360E-RDK boards. Currently U-Boot doesn't fill the clock-frequency property for timer nodes, so for now we have to fill it manually. Signed-off-by: Anton Vorontsov Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/mpc836x_rdk.dts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/boot/dts/mpc836x_rdk.dts b/arch/powerpc/boot/dts/mpc836x_rdk.dts index decadf3d9e98..37b789510d68 100644 --- a/arch/powerpc/boot/dts/mpc836x_rdk.dts +++ b/arch/powerpc/boot/dts/mpc836x_rdk.dts @@ -218,8 +218,23 @@ reg = <0x440 0x40>; interrupts = <12 13 14 15>; interrupt-parent = <&qeic>; - /* filled by u-boot */ - clock-frequency = <0>; + clock-frequency = <166666666>; + }; + + usb@6c0 { + compatible = "fsl,mpc8360-qe-usb", + "fsl,mpc8323-qe-usb"; + reg = <0x6c0 0x40 0x8b00 0x100>; + interrupts = <11>; + interrupt-parent = <&qeic>; + fsl,fullspeed-clock = "clk21"; + gpios = <&qe_pio_b 2 0 /* USBOE */ + &qe_pio_b 3 0 /* USBTP */ + &qe_pio_b 8 0 /* USBTN */ + &qe_pio_b 9 0 /* USBRP */ + &qe_pio_b 11 0 /* USBRN */ + &qe_pio_e 20 0 /* SPEED */ + &qe_pio_e 21 1 /* POWER */>; }; spi@4c0 { -- cgit v1.2.3 From c9dadffbe901bb1d5d54bffbd72ae4ee89a1e08b Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 29 Dec 2008 19:40:32 +0300 Subject: powerpc/fsl_pci: Fix sparse warnings This patch fixes following sparse warnings: CHECK fsl_pci.c fsl_pci.c:32:13: warning: symbol 'setup_pci_atmu' was not declared. Should it be static? fsl_pci.c:89:13: warning: symbol 'setup_pci_cmd' was not declared. Should it be static? fsl_pci.c:133:12: warning: symbol 'fsl_pcie_check_link' was not declared. Should it be static? Signed-off-by: Anton Vorontsov Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/fsl_pci.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c index d5f9ae0f1b75..f611d0369cc8 100644 --- a/arch/powerpc/sysdev/fsl_pci.c +++ b/arch/powerpc/sysdev/fsl_pci.c @@ -29,7 +29,8 @@ #if defined(CONFIG_PPC_85xx) || defined(CONFIG_PPC_86xx) /* atmu setup for fsl pci/pcie controller */ -void __init setup_pci_atmu(struct pci_controller *hose, struct resource *rsrc) +static void __init setup_pci_atmu(struct pci_controller *hose, + struct resource *rsrc) { struct ccsr_pci __iomem *pci; int i; @@ -86,7 +87,7 @@ void __init setup_pci_atmu(struct pci_controller *hose, struct resource *rsrc) out_be32(&pci->piw[2].piwar, PIWAR_2G); } -void __init setup_pci_cmd(struct pci_controller *hose) +static void __init setup_pci_cmd(struct pci_controller *hose) { u16 cmd; int cap_x; @@ -130,7 +131,7 @@ static void __init quirk_fsl_pcie_header(struct pci_dev *dev) return ; } -int __init fsl_pcie_check_link(struct pci_controller *hose) +static int __init fsl_pcie_check_link(struct pci_controller *hose) { u32 val; early_read_config_dword(hose, 0, 0, PCIE_LTSSM, &val); -- cgit v1.2.3 From 20cfb41ba82d9dfc7f09451ab26e826a68a4c896 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Fri, 5 Dec 2008 19:55:04 +0300 Subject: powerpc/83xx: Fix few build errors with CONFIG_QUICC_ENGINE=n Some 83xx boards were not ready for the optional QUICC Engine support. This patch fixes following build errors: arch/powerpc/platforms/built-in.o: In function `flush_disable_caches': (.text+0xb308): undefined reference to `par_io_data_set' arch/powerpc/platforms/built-in.o: In function `flush_disable_caches': (.text+0xb334): undefined reference to `par_io_data_set' arch/powerpc/platforms/built-in.o: In function `flush_disable_caches': (.text+0xb408): undefined reference to `qe_ic_get_high_irq' arch/powerpc/platforms/built-in.o: In function `flush_disable_caches': (.text+0xb478): undefined reference to `qe_ic_get_low_irq' arch/powerpc/platforms/built-in.o: In function `mpc832x_spi_init': mpc832x_rdb.c:(.init.text+0x574c): undefined reference to `par_io_config_pin' mpc832x_rdb.c:(.init.text+0x5768): undefined reference to `par_io_config_pin' mpc832x_rdb.c:(.init.text+0x5784): undefined reference to `par_io_config_pin' mpc832x_rdb.c:(.init.text+0x57a0): undefined reference to `par_io_config_pin' mpc832x_rdb.c:(.init.text+0x57bc): undefined reference to `par_io_config_pin' arch/powerpc/platforms/built-in.o:mpc832x_rdb.c:(.init.text+0x57d8): more undefined references to `par_io_config_pin' follow arch/powerpc/platforms/built-in.o: In function `mpc836x_rdk_init_IRQ': mpc836x_rdk.c:(.init.text+0x5e84): undefined reference to `qe_ic_init' arch/powerpc/platforms/built-in.o: In function `mpc836x_rdk_setup_arch': mpc836x_rdk.c:(.init.text+0x5f10): undefined reference to `qe_reset' make: *** [.tmp_vmlinux1] Error 1 Signed-off-by: Anton Vorontsov Signed-off-by: Kumar Gala --- arch/powerpc/platforms/83xx/mpc832x_rdb.c | 3 ++- arch/powerpc/platforms/83xx/mpc836x_rdk.c | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c index d8eb4473a1bc..2a1295f19832 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -38,6 +38,7 @@ #define DBG(fmt...) #endif +#ifdef CONFIG_QUICC_ENGINE static void mpc83xx_spi_activate_cs(u8 cs, u8 polarity) { pr_debug("%s %d %d\n", __func__, cs, polarity); @@ -77,8 +78,8 @@ static int __init mpc832x_spi_init(void) mpc83xx_spi_activate_cs, mpc83xx_spi_deactivate_cs); } - machine_device_initcall(mpc832x_rdb, mpc832x_spi_init); +#endif /* CONFIG_QUICC_ENGINE */ /* ************************************************************************ * diff --git a/arch/powerpc/platforms/83xx/mpc836x_rdk.c b/arch/powerpc/platforms/83xx/mpc836x_rdk.c index a5273bb28e1b..b0090aac9642 100644 --- a/arch/powerpc/platforms/83xx/mpc836x_rdk.c +++ b/arch/powerpc/platforms/83xx/mpc836x_rdk.c @@ -51,8 +51,9 @@ static void __init mpc836x_rdk_setup_arch(void) for_each_compatible_node(np, "pci", "fsl,mpc8349-pci") mpc83xx_add_bridge(np); #endif - +#ifdef CONFIG_QUICC_ENGINE qe_reset(); +#endif } static void __init mpc836x_rdk_init_IRQ(void) @@ -71,13 +72,14 @@ static void __init mpc836x_rdk_init_IRQ(void) */ ipic_set_default_priority(); of_node_put(np); - +#ifdef CONFIG_QUICC_ENGINE np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic"); if (!np) return; qe_ic_init(np, 0, qe_ic_cascade_low_ipic, qe_ic_cascade_high_ipic); of_node_put(np); +#endif } /* -- cgit v1.2.3 From be11d3b354847bbc41353448dd2b34a2821ddb36 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Fri, 5 Dec 2008 19:59:13 +0300 Subject: powerpc/qe: Fix few build errors with CONFIG_QUICC_ENGINE=n Some 83xx boards were not ready for the optional QUICC Engine support. This patch fixes following build errors: arch/powerpc/platforms/built-in.o: In function `flush_disable_caches': (.text+0xb308): undefined reference to `par_io_data_set' arch/powerpc/platforms/built-in.o: In function `flush_disable_caches': (.text+0xb334): undefined reference to `par_io_data_set' arch/powerpc/platforms/built-in.o: In function `flush_disable_caches': (.text+0xb408): undefined reference to `qe_ic_get_high_irq' arch/powerpc/platforms/built-in.o: In function `flush_disable_caches': (.text+0xb478): undefined reference to `qe_ic_get_low_irq' arch/powerpc/platforms/built-in.o: In function `mpc832x_spi_init': mpc832x_rdb.c:(.init.text+0x574c): undefined reference to `par_io_config_pin' mpc832x_rdb.c:(.init.text+0x5768): undefined reference to `par_io_config_pin' mpc832x_rdb.c:(.init.text+0x5784): undefined reference to `par_io_config_pin' mpc832x_rdb.c:(.init.text+0x57a0): undefined reference to `par_io_config_pin' mpc832x_rdb.c:(.init.text+0x57bc): undefined reference to `par_io_config_pin' arch/powerpc/platforms/built-in.o:mpc832x_rdb.c:(.init.text+0x57d8): more undefined references to `par_io_config_pin' follow arch/powerpc/platforms/built-in.o: In function `mpc836x_rdk_init_IRQ': mpc836x_rdk.c:(.init.text+0x5e84): undefined reference to `qe_ic_init' arch/powerpc/platforms/built-in.o: In function `mpc836x_rdk_setup_arch': mpc836x_rdk.c:(.init.text+0x5f10): undefined reference to `qe_reset' make: *** [.tmp_vmlinux1] Error 1 Signed-off-by: Anton Vorontsov Signed-off-by: Kumar Gala --- arch/powerpc/include/asm/qe.h | 16 ++++++++++++++-- arch/powerpc/include/asm/qe_ic.h | 21 +++++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/include/asm/qe.h b/arch/powerpc/include/asm/qe.h index 32274407b93a..a0a15311d0d8 100644 --- a/arch/powerpc/include/asm/qe.h +++ b/arch/powerpc/include/asm/qe.h @@ -86,7 +86,11 @@ static inline bool qe_clock_is_brg(enum qe_clock clk) extern spinlock_t cmxgcr_lock; /* Export QE common operations */ +#ifdef CONFIG_QUICC_ENGINE extern void __init qe_reset(void); +#else +static inline void qe_reset(void) {} +#endif /* QE PIO */ #define QE_PIO_PINS 32 @@ -103,16 +107,24 @@ struct qe_pio_regs { #endif }; -extern int par_io_init(struct device_node *np); -extern int par_io_of_config(struct device_node *np); #define QE_PIO_DIR_IN 2 #define QE_PIO_DIR_OUT 1 extern void __par_io_config_pin(struct qe_pio_regs __iomem *par_io, u8 pin, int dir, int open_drain, int assignment, int has_irq); +#ifdef CONFIG_QUICC_ENGINE +extern int par_io_init(struct device_node *np); +extern int par_io_of_config(struct device_node *np); extern int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain, int assignment, int has_irq); extern int par_io_data_set(u8 port, u8 pin, u8 val); +#else +static inline int par_io_init(struct device_node *np) { return -ENOSYS; } +static inline int par_io_of_config(struct device_node *np) { return -ENOSYS; } +static inline int par_io_config_pin(u8 port, u8 pin, int dir, int open_drain, + int assignment, int has_irq) { return -ENOSYS; } +static inline int par_io_data_set(u8 port, u8 pin, u8 val) { return -ENOSYS; } +#endif /* CONFIG_QUICC_ENGINE */ /* * Pin multiplexing functions. diff --git a/arch/powerpc/include/asm/qe_ic.h b/arch/powerpc/include/asm/qe_ic.h index 56a7745ca343..cf519663a791 100644 --- a/arch/powerpc/include/asm/qe_ic.h +++ b/arch/powerpc/include/asm/qe_ic.h @@ -17,6 +17,9 @@ #include +struct device_node; +struct qe_ic; + #define NUM_OF_QE_IC_GROUPS 6 /* Flags when we init the QE IC */ @@ -54,17 +57,27 @@ enum qe_ic_grp_id { QE_IC_GRP_RISCB /* QE interrupt controller RISC group B */ }; +#ifdef CONFIG_QUICC_ENGINE void qe_ic_init(struct device_node *node, unsigned int flags, void (*low_handler)(unsigned int irq, struct irq_desc *desc), void (*high_handler)(unsigned int irq, struct irq_desc *desc)); +unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic); +unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic); +#else +static inline void qe_ic_init(struct device_node *node, unsigned int flags, + void (*low_handler)(unsigned int irq, struct irq_desc *desc), + void (*high_handler)(unsigned int irq, struct irq_desc *desc)) +{} +static inline unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic) +{ return 0; } +static inline unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic) +{ return 0; } +#endif /* CONFIG_QUICC_ENGINE */ + void qe_ic_set_highest_priority(unsigned int virq, int high); int qe_ic_set_priority(unsigned int virq, unsigned int priority); int qe_ic_set_high_priority(unsigned int virq, unsigned int priority, int high); -struct qe_ic; -unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic); -unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic); - static inline void qe_ic_cascade_low_ipic(unsigned int irq, struct irq_desc *desc) { -- cgit v1.2.3 From 47f80a325c81a259a110741a7afab572c5550311 Mon Sep 17 00:00:00 2001 From: Becky Bruce Date: Fri, 19 Dec 2008 16:05:12 -0600 Subject: powerpc/86xx: Update 8641hpcn dts file to match latest u-boot The newest revision of uboot reworks the memory map for this board to look more like the 85xx boards. Also, some regions which were far larger than the actual hardware have been scaled back to match the board, and the imaginary second flash bank has been removed. Rapidio and PCI are mutually exclusive in the hardware, and they now are occupying the same space in the address map. The Rapidio node is commented out of the .dts since PCI is the common use case. Signed-off-by: Becky Bruce Signed-off-by: Kumar Gala --- arch/powerpc/boot/dts/mpc8641_hpcn.dts | 56 +++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/arch/powerpc/boot/dts/mpc8641_hpcn.dts b/arch/powerpc/boot/dts/mpc8641_hpcn.dts index d665e767822a..d2d5dcda1b4e 100644 --- a/arch/powerpc/boot/dts/mpc8641_hpcn.dts +++ b/arch/powerpc/boot/dts/mpc8641_hpcn.dts @@ -26,7 +26,13 @@ serial1 = &serial1; pci0 = &pci0; pci1 = &pci1; - rapidio0 = &rapidio0; +/* + * Only one of Rapid IO or PCI can be present due to HW limitations and + * due to the fact that the 2 now share address space in the new memory + * map. The most likely case is that we have PCI, so comment out the + * rapidio node. Leave it here for reference. + */ + /* rapidio0 = &rapidio0; */ }; cpus { @@ -62,18 +68,17 @@ reg = <0x00000000 0x40000000>; // 1G at 0x0 }; - localbus@f8005000 { + localbus@ffe05000 { #address-cells = <2>; #size-cells = <1>; compatible = "fsl,mpc8641-localbus", "simple-bus"; - reg = <0xf8005000 0x1000>; + reg = <0xffe05000 0x1000>; interrupts = <19 2>; interrupt-parent = <&mpic>; - ranges = <0 0 0xff800000 0x00800000 - 1 0 0xfe000000 0x01000000 - 2 0 0xf8200000 0x00100000 - 3 0 0xf8100000 0x00100000>; + ranges = <0 0 0xef800000 0x00800000 + 2 0 0xffdf8000 0x00008000 + 3 0 0xffdf0000 0x00008000>; flash@0,0 { compatible = "cfi-flash"; @@ -103,13 +108,13 @@ }; }; - soc8641@f8000000 { + soc8641@ffe00000 { #address-cells = <1>; #size-cells = <1>; device_type = "soc"; compatible = "simple-bus"; - ranges = <0x00000000 0xf8000000 0x00100000>; - reg = <0xf8000000 0x00001000>; // CCSRBAR + ranges = <0x00000000 0xffe00000 0x00100000>; + reg = <0xffe00000 0x00001000>; // CCSRBAR bus-frequency = <0>; i2c@3000 { @@ -295,17 +300,17 @@ }; }; - pci0: pcie@f8008000 { + pci0: pcie@ffe08000 { cell-index = <0>; compatible = "fsl,mpc8641-pcie"; device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; #address-cells = <3>; - reg = <0xf8008000 0x1000>; + reg = <0xffe08000 0x1000>; bus-range = <0x0 0xff>; ranges = <0x02000000 0x0 0x80000000 0x80000000 0x0 0x20000000 - 0x01000000 0x0 0x00000000 0xe2000000 0x0 0x00100000>; + 0x01000000 0x0 0x00000000 0xffc00000 0x0 0x00010000>; clock-frequency = <33333333>; interrupt-parent = <&mpic>; interrupts = <24 2>; @@ -436,7 +441,7 @@ 0x01000000 0x0 0x00000000 0x01000000 0x0 0x00000000 - 0x0 0x00100000>; + 0x0 0x00010000>; uli1575@0 { reg = <0 0 0 0 0>; #size-cells = <2>; @@ -446,7 +451,7 @@ 0x0 0x20000000 0x01000000 0x0 0x00000000 0x01000000 0x0 0x00000000 - 0x0 0x00100000>; + 0x0 0x00010000>; isa@1e { device_type = "isa"; #interrupt-cells = <2>; @@ -504,17 +509,17 @@ }; - pci1: pcie@f8009000 { + pci1: pcie@ffe09000 { cell-index = <1>; compatible = "fsl,mpc8641-pcie"; device_type = "pci"; #interrupt-cells = <1>; #size-cells = <2>; #address-cells = <3>; - reg = <0xf8009000 0x1000>; + reg = <0xffe09000 0x1000>; bus-range = <0 0xff>; ranges = <0x02000000 0x0 0xa0000000 0xa0000000 0x0 0x20000000 - 0x01000000 0x0 0x00000000 0xe3000000 0x0 0x00100000>; + 0x01000000 0x0 0x00000000 0xffc10000 0x0 0x00010000>; clock-frequency = <33333333>; interrupt-parent = <&mpic>; interrupts = <25 2>; @@ -537,18 +542,21 @@ 0x01000000 0x0 0x00000000 0x01000000 0x0 0x00000000 - 0x0 0x00100000>; + 0x0 0x00010000>; }; }; - rapidio0: rapidio@f80c0000 { +/* + rapidio0: rapidio@ffec0000 { #address-cells = <2>; #size-cells = <2>; compatible = "fsl,rapidio-delta"; - reg = <0xf80c0000 0x20000>; - ranges = <0 0 0xc0000000 0 0x20000000>; + reg = <0xffec0000 0x20000>; + ranges = <0 0 0x80000000 0 0x20000000>; interrupt-parent = <&mpic>; - /* err_irq bell_outb_irq bell_inb_irq - msg1_tx_irq msg1_rx_irq msg2_tx_irq msg2_rx_irq */ + // err_irq bell_outb_irq bell_inb_irq + // msg1_tx_irq msg1_rx_irq msg2_tx_irq msg2_rx_irq interrupts = <48 2 49 2 50 2 53 2 54 2 55 2 56 2>; }; +*/ + }; -- cgit v1.2.3 From 00c4b95c44692f84b565cfea0af582c0677acaa7 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Mon, 1 Dec 2008 14:40:46 -0600 Subject: powerpc/85xx: Enable SMP support Signed-off-by: Kumar Gala --- arch/powerpc/platforms/Kconfig.cputype | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 3d0c776f888d..e868b5c50723 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -231,7 +231,7 @@ config VIRT_CPU_ACCOUNTING If in doubt, say Y here. config SMP - depends on PPC_STD_MMU + depends on PPC_STD_MMU || FSL_BOOKE bool "Symmetric multi-processing support" ---help--- This enables support for systems with more than one CPU. If you have -- cgit v1.2.3 From 8bd3947afda14625bff8c067b760840abfb51358 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Wed, 19 Nov 2008 09:25:29 -0600 Subject: powerpc/85xx: Add SMP support to MPC8572 DS Enable SMP support on the MPC8572 DS reference board. Signed-off-by: Kumar Gala --- arch/powerpc/configs/85xx/mpc8572_ds_defconfig | 43 +++++++++++++++++--------- arch/powerpc/platforms/85xx/mpc85xx_ds.c | 7 +++++ 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/arch/powerpc/configs/85xx/mpc8572_ds_defconfig b/arch/powerpc/configs/85xx/mpc8572_ds_defconfig index 635588319e0d..32aeb79216f7 100644 --- a/arch/powerpc/configs/85xx/mpc8572_ds_defconfig +++ b/arch/powerpc/configs/85xx/mpc8572_ds_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.28-rc3 -# Sat Nov 8 12:40:13 2008 +# Linux kernel version: 2.6.28-rc8 +# Tue Dec 30 11:17:46 2008 # # CONFIG_PPC64 is not set @@ -21,7 +21,10 @@ CONFIG_FSL_BOOKE=y CONFIG_FSL_EMB_PERFMON=y # CONFIG_PHYS_64BIT is not set CONFIG_SPE=y +CONFIG_PPC_MMU_NOHASH=y # CONFIG_PPC_MM_SLICES is not set +CONFIG_SMP=y +CONFIG_NR_CPUS=2 CONFIG_PPC32=y CONFIG_WORD_SIZE=32 # CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set @@ -50,7 +53,7 @@ CONFIG_ARCH_MAY_HAVE_PC_FDC=y CONFIG_PPC_OF=y CONFIG_OF=y CONFIG_PPC_UDBG_16550=y -# CONFIG_GENERIC_TBSYNC is not set +CONFIG_GENERIC_TBSYNC=y CONFIG_AUDIT_ARCH=y CONFIG_GENERIC_BUG=y CONFIG_DEFAULT_UIMAGE=y @@ -62,7 +65,7 @@ CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" # General setup # CONFIG_EXPERIMENTAL=y -CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_LOCALVERSION="" CONFIG_LOCALVERSION_AUTO=y @@ -126,6 +129,7 @@ CONFIG_HAVE_IOREMAP_PROT=y CONFIG_HAVE_KPROBES=y CONFIG_HAVE_KRETPROBES=y CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_USE_GENERIC_SMP_HELPERS=y # CONFIG_HAVE_GENERIC_DMA_COHERENT is not set CONFIG_SLABINFO=y CONFIG_RT_MUTEXES=y @@ -138,6 +142,7 @@ CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y # CONFIG_MODULE_SRCVERSION_ALL is not set CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y CONFIG_BLOCK=y CONFIG_LBD=y # CONFIG_BLK_DEV_IO_TRACE is not set @@ -197,6 +202,7 @@ CONFIG_PPC_I8259=y # CONFIG_CPM2 is not set CONFIG_FSL_ULI1575=y # CONFIG_MPC8xxx_GPIO is not set +# CONFIG_SIMPLE_GPIO is not set # # Kernel options @@ -224,6 +230,7 @@ CONFIG_MATH_EMULATION=y CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y CONFIG_ARCH_HAS_WALK_MEMORY=y CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y +# CONFIG_IRQ_ALL_CPUS is not set CONFIG_ARCH_FLATMEM_ENABLE=y CONFIG_ARCH_POPULATES_NODE_MAP=y CONFIG_SELECT_MEMORY_MODEL=y @@ -241,6 +248,9 @@ CONFIG_ZONE_DMA_FLAG=1 CONFIG_BOUNCE=y CONFIG_VIRT_TO_BUS=y CONFIG_UNEVICTABLE_LRU=y +CONFIG_PPC_4K_PAGES=y +# CONFIG_PPC_16K_PAGES is not set +# CONFIG_PPC_64K_PAGES is not set CONFIG_FORCE_MAX_ZONEORDER=11 CONFIG_PROC_DEVICETREE=y # CONFIG_CMDLINE_BOOL is not set @@ -443,8 +453,10 @@ CONFIG_MISC_DEVICES=y # CONFIG_EEPROM_93CX6 is not set # CONFIG_SGI_IOC4 is not set # CONFIG_TIFM_CORE is not set +# CONFIG_ICS932S401 is not set # CONFIG_ENCLOSURE_SERVICES is not set # CONFIG_HP_ILO is not set +# CONFIG_C2PORT is not set CONFIG_HAVE_IDE=y # CONFIG_IDE is not set @@ -784,6 +796,7 @@ CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y CONFIG_LEGACY_PTYS=y CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_HVC_UDBG is not set # CONFIG_IPMI_HANDLER is not set CONFIG_HW_RANDOM=y CONFIG_NVRAM=y @@ -869,11 +882,11 @@ CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y # CONFIG_THERMAL is not set # CONFIG_THERMAL_HWMON is not set # CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y # # Sonics Silicon Backplane # -CONFIG_SSB_POSSIBLE=y # CONFIG_SSB is not set # @@ -886,14 +899,7 @@ CONFIG_SSB_POSSIBLE=y # CONFIG_PMIC_DA903X is not set # CONFIG_MFD_WM8400 is not set # CONFIG_MFD_WM8350_I2C is not set - -# -# Voltage and Current regulators -# # CONFIG_REGULATOR is not set -# CONFIG_REGULATOR_FIXED_VOLTAGE is not set -# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set -# CONFIG_REGULATOR_BQ24022 is not set # # Multimedia devices @@ -1252,11 +1258,11 @@ CONFIG_USB_OHCI_LITTLE_ENDIAN=y # CONFIG_USB_TMC is not set # -# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed; # # -# may also be needed; see USB_STORAGE Help for more information +# see USB_STORAGE Help for more information # CONFIG_USB_STORAGE=y # CONFIG_USB_STORAGE_DEBUG is not set @@ -1348,6 +1354,7 @@ CONFIG_RTC_INTF_DEV=y # CONFIG_RTC_DRV_M41T80 is not set # CONFIG_RTC_DRV_S35390A is not set # CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set # # SPI RTC drivers @@ -1624,6 +1631,7 @@ CONFIG_HAVE_FUNCTION_TRACER=y # CONFIG_SAMPLES is not set CONFIG_HAVE_ARCH_KGDB=y # CONFIG_KGDB is not set +CONFIG_PRINT_STACK_DEPTH=64 # CONFIG_DEBUG_STACKOVERFLOW is not set # CONFIG_DEBUG_STACK_USAGE is not set # CONFIG_DEBUG_PAGEALLOC is not set @@ -1649,11 +1657,16 @@ CONFIG_CRYPTO=y # # CONFIG_CRYPTO_FIPS is not set CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y CONFIG_CRYPTO_HASH=y -CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y # CONFIG_CRYPTO_GF128MUL is not set # CONFIG_CRYPTO_NULL is not set # CONFIG_CRYPTO_CRYPTD is not set diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ds.c b/arch/powerpc/platforms/85xx/mpc85xx_ds.c index a8301c8ad537..7326d904202c 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_ds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_ds.c @@ -148,6 +148,9 @@ static int mpc85xx_exclude_device(struct pci_controller *hose, /* * Setup the architecture */ +#ifdef CONFIG_SMP +extern void __init mpc85xx_smp_init(void); +#endif static void __init mpc85xx_ds_setup_arch(void) { #ifdef CONFIG_PCI @@ -173,6 +176,10 @@ static void __init mpc85xx_ds_setup_arch(void) ppc_md.pci_exclude_device = mpc85xx_exclude_device; #endif +#ifdef CONFIG_SMP + mpc85xx_smp_init(); +#endif + printk("MPC85xx DS board from Freescale Semiconductor\n"); } -- cgit v1.2.3 From 870029a682d468585a80ce782871f3f220cfef0f Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 21 Dec 2008 21:54:45 +0100 Subject: powerpc/85xx: Add local_irq_restore in error handling code There is a call to local_irq_restore in the normal exit case, so it would seem that there should be one on an error return as well. The semantic patch that makes this change is as follows: (http://www.emn.fr/x-info/coccinelle/) // @@ expression l; expression E,E1,E2; @@ local_irq_save(l); ... when != local_irq_restore(l) when != spin_unlock_irqrestore(E,l) when any when strict ( if (...) { ... when != local_irq_restore(l) when != spin_unlock_irqrestore(E1,l) + local_irq_restore(l); return ...; } | if (...) + {local_irq_restore(l); return ...; + } | spin_unlock_irqrestore(E2,l); | local_irq_restore(l); ) // Signed-off-by: Julia Lawall Signed-off-by: Kumar Gala --- arch/powerpc/platforms/85xx/smp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c index d652c713f496..79a0df17078b 100644 --- a/arch/powerpc/platforms/85xx/smp.c +++ b/arch/powerpc/platforms/85xx/smp.c @@ -58,6 +58,7 @@ smp_85xx_kick_cpu(int nr) if (cpu_rel_addr == NULL) { printk(KERN_ERR "No cpu-release-addr for cpu %d\n", nr); + local_irq_restore(flags); return; } -- cgit v1.2.3 From 068e8c9d02ee37c44a4d65279b3ae8188fb09e18 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sat, 8 Nov 2008 20:52:54 +0300 Subject: powerpc/qe: Select QE_USB with USB_GADGET_FSL_QE Boards should know when QE_USB is used, so that they can configure USB clocks and pins. Another option would be to add 'select QE_USB' into USB_GADGET_FSL_QE, but selects are evil since they don't support dependencies. While at it, also remove 'host' from the symbol description, since the QE_USB code is used to support the gadget driver as well. Signed-off-by: Anton Vorontsov Signed-off-by: Kumar Gala --- arch/powerpc/sysdev/qe_lib/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/sysdev/qe_lib/Kconfig b/arch/powerpc/sysdev/qe_lib/Kconfig index 76ffbc48d4b9..41ac3dfac98e 100644 --- a/arch/powerpc/sysdev/qe_lib/Kconfig +++ b/arch/powerpc/sysdev/qe_lib/Kconfig @@ -22,5 +22,6 @@ config UCC config QE_USB bool + default y if USB_GADGET_FSL_QE help - QE USB Host Controller support + QE USB Controller support -- cgit v1.2.3 From c1f343028d35ba4e88cd4a3c44e0d8b8a84264ee Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 11 Nov 2008 17:45:52 +0000 Subject: powerpc/pci: Reserve legacy regions on PCI There's a problem on some embedded platforms when we re-assign everything on PCI, such as 44x. The generic code tries to avoid assigning devices to addresses overlapping the low legacy addresses such as VGA hard decoded areas using constants that are unfortunately no good for us, as they don't take into account the address translation we do to access PCI busses. Thus we end up allocating things like IO BARs to 0, which is technically legal, but will shadow hard decoded ports for use by things like VGA cards. This works around it by attempting to reserve legacy regions before we try to assign addresses. NOTE: This may have nasty side effects in cases I haven't tested yet: - We try to use FW mappings (ie. powermac) and the FW has allocated a conflicting address over those legacy regions. This will typically happen. I would expect the new code to just fail with an informative message without harm but I haven't had a chance to test that scenario yet. - A device with fixed BARs overlapping those legacy addresses such as an IDE controller in legacy mode is in the system. I don't know for sure yet what will happen there, I have to test :-) Ideally, we should change PCIBIOS_MIN_IO/MIN_MEM accross the board to take a bus pointer so they can provide appropriate per-bus translated values to the generic code but that's a more invasive patch. I will do that in the future, but in the meantime, this fixes the problem locally Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/pci-common.c | 71 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 2538030954d8..da5a3855a0c4 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -16,7 +16,7 @@ * 2 of the License, or (at your option) any later version. */ -#undef DEBUG +#define DEBUG #include #include @@ -1356,6 +1356,63 @@ static void __init pcibios_allocate_resources(int pass) } } +static void __init pcibios_reserve_legacy_regions(struct pci_bus *bus) +{ + struct pci_controller *hose = pci_bus_to_host(bus); + resource_size_t offset; + struct resource *res, *pres; + int i; + + pr_debug("Reserving legacy ranges for domain %04x\n", pci_domain_nr(bus)); + + /* Check for IO */ + if (!(hose->io_resource.flags & IORESOURCE_IO)) + goto no_io; + offset = (unsigned long)hose->io_base_virt - _IO_BASE; + res = kzalloc(sizeof(struct resource), GFP_KERNEL); + BUG_ON(res == NULL); + res->name = "Legacy IO"; + res->flags = IORESOURCE_IO; + res->start = offset; + res->end = (offset + 0xfff) & 0xfffffffful; + pr_debug("Candidate legacy IO: %pR\n", res); + if (request_resource(&hose->io_resource, res)) { + printk(KERN_DEBUG + "PCI %04x:%02x Cannot reserve Legacy IO %pR\n", + pci_domain_nr(bus), bus->number, res); + kfree(res); + } + + no_io: + /* Check for memory */ + offset = hose->pci_mem_offset; + pr_debug("hose mem offset: %016llx\n", (unsigned long long)offset); + for (i = 0; i < 3; i++) { + pres = &hose->mem_resources[i]; + if (!(pres->flags & IORESOURCE_MEM)) + continue; + pr_debug("hose mem res: %pR\n", pres); + if ((pres->start - offset) <= 0xa0000 && + (pres->end - offset) >= 0xbffff) + break; + } + if (i >= 3) + return; + res = kzalloc(sizeof(struct resource), GFP_KERNEL); + BUG_ON(res == NULL); + res->name = "Legacy VGA memory"; + res->flags = IORESOURCE_MEM; + res->start = 0xa0000 + offset; + res->end = 0xbffff + offset; + pr_debug("Candidate VGA memory: %pR\n", res); + if (request_resource(pres, res)) { + printk(KERN_DEBUG + "PCI %04x:%02x Cannot reserve VGA memory %pR\n", + pci_domain_nr(bus), bus->number, res); + kfree(res); + } +} + void __init pcibios_resource_survey(void) { struct pci_bus *b; @@ -1371,6 +1428,18 @@ void __init pcibios_resource_survey(void) pcibios_allocate_resources(1); } + /* Before we start assigning unassigned resource, we try to reserve + * the low IO area and the VGA memory area if they intersect the + * bus available resources to avoid allocating things on top of them + */ + if (!(ppc_pci_flags & PPC_PCI_PROBE_ONLY)) { + list_for_each_entry(b, &pci_root_buses, node) + pcibios_reserve_legacy_regions(b); + } + + /* Now, if the platform didn't decide to blindly trust the firmware, + * we proceed to assigning things that were left unassigned + */ if (!(ppc_pci_flags & PPC_PCI_PROBE_ONLY)) { pr_debug("PCI: Assigning unassigned resouces...\n"); pci_assign_unassigned_resources(); -- cgit v1.2.3 From afcb065450913745027169d906b9afc8294f7007 Mon Sep 17 00:00:00 2001 From: Nicolas Palix Date: Wed, 3 Dec 2008 00:25:03 +0000 Subject: powerpc/powermac: Add missing of_node_put This patch fixes some unbalanced OF node references in the powermac code Signed-off-by: Nicolas Palix Signed-off-by: Julia Lawall Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/powermac/pci.c | 2 ++ arch/powerpc/platforms/powermac/time.c | 11 +++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c index 54b7b76ed4f0..04cdd32624d4 100644 --- a/arch/powerpc/platforms/powermac/pci.c +++ b/arch/powerpc/platforms/powermac/pci.c @@ -661,6 +661,7 @@ static void __init init_second_ohare(void) pci_find_hose_for_OF_device(np); if (!hose) { printk(KERN_ERR "Can't find PCI hose for OHare2 !\n"); + of_node_put(np); return; } early_read_config_word(hose, bus, devfn, PCI_COMMAND, &cmd); @@ -669,6 +670,7 @@ static void __init init_second_ohare(void) early_write_config_word(hose, bus, devfn, PCI_COMMAND, cmd); } has_second_ohare = 1; + of_node_put(np); } /* diff --git a/arch/powerpc/platforms/powermac/time.c b/arch/powerpc/platforms/powermac/time.c index 59eb840d8ce2..1810e4226e56 100644 --- a/arch/powerpc/platforms/powermac/time.c +++ b/arch/powerpc/platforms/powermac/time.c @@ -265,12 +265,15 @@ int __init via_calibrate_decr(void) struct resource rsrc; vias = of_find_node_by_name(NULL, "via-cuda"); - if (vias == 0) + if (vias == NULL) vias = of_find_node_by_name(NULL, "via-pmu"); - if (vias == 0) + if (vias == NULL) vias = of_find_node_by_name(NULL, "via"); - if (vias == 0 || of_address_to_resource(vias, 0, &rsrc)) + if (vias == NULL || of_address_to_resource(vias, 0, &rsrc)) { + of_node_put(vias); return 0; + } + of_node_put(vias); via = ioremap(rsrc.start, rsrc.end - rsrc.start + 1); if (via == NULL) { printk(KERN_ERR "Failed to map VIA for timer calibration !\n"); @@ -297,7 +300,7 @@ int __init via_calibrate_decr(void) ppc_tb_freq = (dstart - dend) * 100 / 6; iounmap(via); - + return 1; } #endif -- cgit v1.2.3 From c555e520ef794a94dc36a8ded93ece6369ff7ca0 Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Tue, 9 Dec 2008 08:21:32 +0000 Subject: powerpc/mm: Add better comment on careful_allocation() The behavior in careful_allocation() really confused me at first. Add a comment to hopefully make it easier on the next doofus that looks at it. Signed-off-by: Dave Hansen Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/numa.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index cf81049e1e51..213664c9cdca 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -840,8 +840,16 @@ static void __init *careful_allocation(int nid, unsigned long size, size, nid); /* - * If the memory came from a previously allocated node, we must - * retry with the bootmem allocator. + * We initialize the nodes in numeric order: 0, 1, 2... + * and hand over control from the LMB allocator to the + * bootmem allocator. If this function is called for + * node 5, then we know that all nodes <5 are using the + * bootmem allocator instead of the LMB allocator. + * + * So, check the nid from which this allocation came + * and double check to see if we need to use bootmem + * instead of the LMB. We don't free the LMB memory + * since it would be useless. */ new_nid = early_pfn_to_nid(ret >> PAGE_SHIFT); if (new_nid < nid) { -- cgit v1.2.3 From 5d21ea2b0e1d9d5d880670dbb9a96efe9b419583 Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Tue, 9 Dec 2008 08:21:33 +0000 Subject: powerpc/mm:: Cleanup careful_allocation(): bootmem already panics If we fail a bootmem allocation, the bootmem code itself panics. No need to redo it here. Also change the wording of the other panic. We don't strictly have to allocate memory on the specified node. It is just a hint and that node may not even *have* any memory on it. In that case we can and do fall back to other nodes. Signed-off-by: Dave Hansen Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/numa.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 213664c9cdca..aabf30175eb5 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -836,7 +836,7 @@ static void __init *careful_allocation(int nid, unsigned long size, ret = __lmb_alloc_base(size, align, lmb_end_of_DRAM()); if (!ret) - panic("numa.c: cannot allocate %lu bytes on node %d", + panic("numa.c: cannot allocate %lu bytes for node %d", size, nid); /* @@ -856,10 +856,6 @@ static void __init *careful_allocation(int nid, unsigned long size, ret = (unsigned long)__alloc_bootmem_node(NODE_DATA(new_nid), size, align, 0); - if (!ret) - panic("numa.c: cannot allocate %lu bytes on node %d", - size, new_nid); - ret = __pa(ret); dbg("alloc_bootmem %lx %lx\n", ret, size); -- cgit v1.2.3 From 0be210fd664b07531cb238bafb453a2a54c2a7a8 Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Tue, 9 Dec 2008 08:21:35 +0000 Subject: powerpc/mm: Make careful_allocation() return virtual addrs Since we memset() the result in both of the uses here, just make careful_alloc() return a virtual address. Also, add a separate variable to store the physial address that comes back from the lmb_alloc() functions. This makes it less likely that someone will screw it up forgetting to convert before returning since the vaddr is always in a void* and the paddr is always in an unsigned long. I admit this is arbitrary since one of its users needs a paddr and one a vaddr, but it does remove a good number of casts. Signed-off-by: Dave Hansen Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/numa.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index aabf30175eb5..9ec9939f9fb0 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -822,23 +822,28 @@ static void __init dump_numa_memory_topology(void) * required. nid is the preferred node and end is the physical address of * the highest address in the node. * - * Returns the physical address of the memory. + * Returns the virtual address of the memory. */ static void __init *careful_allocation(int nid, unsigned long size, unsigned long align, unsigned long end_pfn) { + void *ret; int new_nid; - unsigned long ret = __lmb_alloc_base(size, align, end_pfn << PAGE_SHIFT); + unsigned long ret_paddr; + + ret_paddr = __lmb_alloc_base(size, align, end_pfn << PAGE_SHIFT); /* retry over all memory */ - if (!ret) - ret = __lmb_alloc_base(size, align, lmb_end_of_DRAM()); + if (!ret_paddr) + ret_paddr = __lmb_alloc_base(size, align, lmb_end_of_DRAM()); - if (!ret) + if (!ret_paddr) panic("numa.c: cannot allocate %lu bytes for node %d", size, nid); + ret = __va(ret_paddr); + /* * We initialize the nodes in numeric order: 0, 1, 2... * and hand over control from the LMB allocator to the @@ -851,17 +856,15 @@ static void __init *careful_allocation(int nid, unsigned long size, * instead of the LMB. We don't free the LMB memory * since it would be useless. */ - new_nid = early_pfn_to_nid(ret >> PAGE_SHIFT); + new_nid = early_pfn_to_nid(ret_paddr >> PAGE_SHIFT); if (new_nid < nid) { - ret = (unsigned long)__alloc_bootmem_node(NODE_DATA(new_nid), + ret = __alloc_bootmem_node(NODE_DATA(new_nid), size, align, 0); - ret = __pa(ret); - - dbg("alloc_bootmem %lx %lx\n", ret, size); + dbg("alloc_bootmem %p %lx\n", ret, size); } - return (void *)ret; + return ret; } static struct notifier_block __cpuinitdata ppc64_numa_nb = { @@ -956,7 +959,7 @@ void __init do_init_bootmem(void) for_each_online_node(nid) { unsigned long start_pfn, end_pfn; - unsigned long bootmem_paddr; + void *bootmem_vaddr; unsigned long bootmap_pages; get_pfn_range_for_nid(nid, &start_pfn, &end_pfn); @@ -971,7 +974,6 @@ void __init do_init_bootmem(void) NODE_DATA(nid) = careful_allocation(nid, sizeof(struct pglist_data), SMP_CACHE_BYTES, end_pfn); - NODE_DATA(nid) = __va(NODE_DATA(nid)); memset(NODE_DATA(nid), 0, sizeof(struct pglist_data)); dbg("node %d\n", nid); @@ -988,14 +990,15 @@ void __init do_init_bootmem(void) dbg("end_paddr = %lx\n", end_pfn << PAGE_SHIFT); bootmap_pages = bootmem_bootmap_pages(end_pfn - start_pfn); - bootmem_paddr = (unsigned long)careful_allocation(nid, + bootmem_vaddr = careful_allocation(nid, bootmap_pages << PAGE_SHIFT, PAGE_SIZE, end_pfn); - memset(__va(bootmem_paddr), 0, bootmap_pages << PAGE_SHIFT); + memset(bootmem_vaddr, 0, bootmap_pages << PAGE_SHIFT); - dbg("bootmap_paddr = %lx\n", bootmem_paddr); + dbg("bootmap_vaddr = %p\n", bootmem_vaddr); - init_bootmem_node(NODE_DATA(nid), bootmem_paddr >> PAGE_SHIFT, + init_bootmem_node(NODE_DATA(nid), + __pa(bootmem_vaddr) >> PAGE_SHIFT, start_pfn, end_pfn); free_bootmem_with_active_regions(nid, end_pfn); -- cgit v1.2.3 From 893473df78b4407c9ab75cb55479409795953b01 Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Tue, 9 Dec 2008 08:21:36 +0000 Subject: powerpc/mm: Cleanup careful_allocation(): consolidate memset() Both users of careful_allocation() immediately memset() the result. So, just do it in one place. Also give careful_allocation() a 'z' prefix to bring it in line with kzmalloc() and friends. Signed-off-by: Dave Hansen Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/numa.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 9ec9939f9fb0..7393bd76d698 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -824,7 +824,7 @@ static void __init dump_numa_memory_topology(void) * * Returns the virtual address of the memory. */ -static void __init *careful_allocation(int nid, unsigned long size, +static void __init *careful_zallocation(int nid, unsigned long size, unsigned long align, unsigned long end_pfn) { @@ -864,6 +864,7 @@ static void __init *careful_allocation(int nid, unsigned long size, dbg("alloc_bootmem %p %lx\n", ret, size); } + memset(ret, 0, size); return ret; } @@ -971,10 +972,9 @@ void __init do_init_bootmem(void) * previous nodes' bootmem to be initialized and have * all reserved areas marked. */ - NODE_DATA(nid) = careful_allocation(nid, + NODE_DATA(nid) = careful_zallocation(nid, sizeof(struct pglist_data), SMP_CACHE_BYTES, end_pfn); - memset(NODE_DATA(nid), 0, sizeof(struct pglist_data)); dbg("node %d\n", nid); dbg("NODE_DATA() = %p\n", NODE_DATA(nid)); @@ -990,10 +990,9 @@ void __init do_init_bootmem(void) dbg("end_paddr = %lx\n", end_pfn << PAGE_SHIFT); bootmap_pages = bootmem_bootmap_pages(end_pfn - start_pfn); - bootmem_vaddr = careful_allocation(nid, + bootmem_vaddr = careful_zallocation(nid, bootmap_pages << PAGE_SHIFT, PAGE_SIZE, end_pfn); - memset(bootmem_vaddr, 0, bootmap_pages << PAGE_SHIFT); dbg("bootmap_vaddr = %p\n", bootmem_vaddr); @@ -1004,7 +1003,7 @@ void __init do_init_bootmem(void) free_bootmem_with_active_regions(nid, end_pfn); /* * Be very careful about moving this around. Future - * calls to careful_allocation() depend on this getting + * calls to careful_zallocation() depend on this getting * done correctly. */ mark_reserved_regions_for_nid(nid); -- cgit v1.2.3 From 29f1aff2cc20d8b81fe3c890b8f134e84b8f41fe Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 19 Dec 2008 14:57:20 +0000 Subject: powerpc: Copy bootable images in the default install script This patch makes the default install script (arch/powerpc/boot/install.sh) copy the bootable image files into the install directory. Before this patch only the vmlinux image file was copied. This patch makes the default 'make install' command useful for embedded development when $(INSTALL_PATH) is set in the environment. As a side effect, this patch changes the calling convention of the install.sh script. Instead of a single 5th parameter, the script is now passed a list of all the target images stored in the $(image-y) Makefile variable. This should be backwards compatible with existing install scripts since it just adds additional arguments and does not change existing ones. Signed-off-by: Grant Likely Acked-by: Josh Boyer Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/boot/Makefile | 2 +- arch/powerpc/boot/install.sh | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index ab6dda372438..e84df338ea29 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -356,7 +356,7 @@ $(obj)/zImage.initrd: $(addprefix $(obj)/, $(initrd-y)) @rm -f $@; ln $< $@ install: $(CONFIGURE) $(addprefix $(obj)/, $(image-y)) - sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)" $< + sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)" $^ # anything not in $(targets) clean-files += $(image-) $(initrd-) cuImage.* dtbImage.* treeImage.* \ diff --git a/arch/powerpc/boot/install.sh b/arch/powerpc/boot/install.sh index b002bfd56786..51b2387bdba0 100644 --- a/arch/powerpc/boot/install.sh +++ b/arch/powerpc/boot/install.sh @@ -15,7 +15,7 @@ # $2 - kernel image file # $3 - kernel map file # $4 - default install path (blank if root directory) -# $5 - kernel boot file, the zImage +# $5 and more - kernel boot files; zImage*, uImage, cuImage.*, etc. # # User may have a custom install script @@ -38,3 +38,15 @@ fi cat $2 > $4/$image_name cp $3 $4/System.map + +# Copy all the bootable image files +path=$4 +shift 4 +while [ $# -ne 0 ]; do + image_name=`basename $1` + if [ -f $path/$image_name ]; then + mv $path/$image_name $path/$image_name.old + fi + cat $1 > $path/$image_name + shift +done; -- cgit v1.2.3 From 5c9a2606bcad101e169012d9f79ab3aed60926aa Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Mon, 22 Dec 2008 23:23:18 +0000 Subject: powerpc/iseries: Kexec is known not to work on iseries The non-zero return from the prepare callback is returned by sys_kexec_load() to userspace, indicating that kexec is not supported on the machine. Signed-off-by: Michael Ellerman Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/iseries/setup.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c index 70b688c1aefb..24519b96d6ad 100644 --- a/arch/powerpc/platforms/iseries/setup.c +++ b/arch/powerpc/platforms/iseries/setup.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -638,6 +639,13 @@ static int __init iseries_probe(void) return 1; } +#ifdef CONFIG_KEXEC +static int iseries_kexec_prepare(struct kimage *image) +{ + return -ENOSYS; +} +#endif + define_machine(iseries) { .name = "iSeries", .setup_arch = iSeries_setup_arch, @@ -658,6 +666,9 @@ define_machine(iseries) { .probe = iseries_probe, .ioremap = iseries_ioremap, .iounmap = iseries_iounmap, +#ifdef CONFIG_KEXEC + .machine_kexec_prepare = iseries_kexec_prepare, +#endif /* XXX Implement enable_pmcs for iSeries */ }; -- cgit v1.2.3 From 93197a36a9c16a85fb24cf5a8639f7bf9af838a3 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Tue, 23 Dec 2008 18:55:54 +0000 Subject: powerpc: Rewrite sysfs processor cache info code The current code for providing processor cache information in sysfs has the following deficiencies: - several complex functions that are hard to understand - implicit recursion (cache_desc_release -> kobject_put -> cache_desc_release) - explicit recursion (create_cache_index_info) - use of two per-cpu arrays when one would suffice - duplication of work on systems where CPUs share cache Also, when I looked at implementing support for a shared_cpu_map attribute, it was pretty much impossible to handle hotplug without checking every single online CPU's cache_desc list and fixing things up... not that this is a hot path, but it would have introduced O(n^2)-ish behavior during boot. Addressing this involved rethinking the core data structures used, which didn't lend itself to an incremental approach. This implementation maintains a "forest" (potentially more than one tree) of cache objects which reflects the system's cache topology. Cache objects are instantiated as needed as CPUs come online. A per-cpu array is used mainly for sysfs-related bookkeeping; the objects in the array just point to the appropriate points in the forest. This maintains compatibility with the existing code and includes some enhancements: - Implement the shared_cpu_map attribute, which is essential for enabling userspace to discover the system's overall cache topology. - Use cache-block-size properties if cache-line-size is not available. I chose to place this implementation in a new file since it would have roughly doubled the size of sysfs.c, which is already kind of messy. Signed-off-by: Nathan Lynch Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/Makefile | 2 +- arch/powerpc/kernel/cacheinfo.c | 837 ++++++++++++++++++++++++++++++++++++++++ arch/powerpc/kernel/cacheinfo.h | 8 + arch/powerpc/kernel/sysfs.c | 300 +------------- 4 files changed, 850 insertions(+), 297 deletions(-) create mode 100644 arch/powerpc/kernel/cacheinfo.c create mode 100644 arch/powerpc/kernel/cacheinfo.h diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 1308a86e9070..8d1a419df35d 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -29,7 +29,7 @@ endif obj-y := cputable.o ptrace.o syscalls.o \ irq.o align.o signal_32.o pmc.o vdso.o \ init_task.o process.o systbl.o idle.o \ - signal.o sysfs.o + signal.o sysfs.o cacheinfo.o obj-y += vdso32/ obj-$(CONFIG_PPC64) += setup_64.o sys_ppc32.o \ signal_64.o ptrace32.o \ diff --git a/arch/powerpc/kernel/cacheinfo.c b/arch/powerpc/kernel/cacheinfo.c new file mode 100644 index 000000000000..b33f0417a4bf --- /dev/null +++ b/arch/powerpc/kernel/cacheinfo.c @@ -0,0 +1,837 @@ +/* + * Processor cache information made available to userspace via sysfs; + * intended to be compatible with x86 intel_cacheinfo implementation. + * + * Copyright 2008 IBM Corporation + * Author: Nathan Lynch + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cacheinfo.h" + +/* per-cpu object for tracking: + * - a "cache" kobject for the top-level directory + * - a list of "index" objects representing the cpu's local cache hierarchy + */ +struct cache_dir { + struct kobject *kobj; /* bare (not embedded) kobject for cache + * directory */ + struct cache_index_dir *index; /* list of index objects */ +}; + +/* "index" object: each cpu's cache directory has an index + * subdirectory corresponding to a cache object associated with the + * cpu. This object's lifetime is managed via the embedded kobject. + */ +struct cache_index_dir { + struct kobject kobj; + struct cache_index_dir *next; /* next index in parent directory */ + struct cache *cache; +}; + +/* Template for determining which OF properties to query for a given + * cache type */ +struct cache_type_info { + const char *name; + const char *size_prop; + + /* Allow for both [di]-cache-line-size and + * [di]-cache-block-size properties. According to the PowerPC + * Processor binding, -line-size should be provided if it + * differs from the cache block size (that which is operated + * on by cache instructions), so we look for -line-size first. + * See cache_get_line_size(). */ + + const char *line_size_props[2]; + const char *nr_sets_prop; +}; + +/* These are used to index the cache_type_info array. */ +#define CACHE_TYPE_UNIFIED 0 +#define CACHE_TYPE_INSTRUCTION 1 +#define CACHE_TYPE_DATA 2 + +static const struct cache_type_info cache_type_info[] = { + { + /* PowerPC Processor binding says the [di]-cache-* + * must be equal on unified caches, so just use + * d-cache properties. */ + .name = "Unified", + .size_prop = "d-cache-size", + .line_size_props = { "d-cache-line-size", + "d-cache-block-size", }, + .nr_sets_prop = "d-cache-sets", + }, + { + .name = "Instruction", + .size_prop = "i-cache-size", + .line_size_props = { "i-cache-line-size", + "i-cache-block-size", }, + .nr_sets_prop = "i-cache-sets", + }, + { + .name = "Data", + .size_prop = "d-cache-size", + .line_size_props = { "d-cache-line-size", + "d-cache-block-size", }, + .nr_sets_prop = "d-cache-sets", + }, +}; + +/* Cache object: each instance of this corresponds to a distinct cache + * in the system. There are separate objects for Harvard caches: one + * each for instruction and data, and each refers to the same OF node. + * The refcount of the OF node is elevated for the lifetime of the + * cache object. A cache object is released when its shared_cpu_map + * is cleared (see cache_cpu_clear). + * + * A cache object is on two lists: an unsorted global list + * (cache_list) of cache objects; and a singly-linked list + * representing the local cache hierarchy, which is ordered by level + * (e.g. L1d -> L1i -> L2 -> L3). + */ +struct cache { + struct device_node *ofnode; /* OF node for this cache, may be cpu */ + struct cpumask shared_cpu_map; /* online CPUs using this cache */ + int type; /* split cache disambiguation */ + int level; /* level not explicit in device tree */ + struct list_head list; /* global list of cache objects */ + struct cache *next_local; /* next cache of >= level */ +}; + +static DEFINE_PER_CPU(struct cache_dir *, cache_dir); + +/* traversal/modification of this list occurs only at cpu hotplug time; + * access is serialized by cpu hotplug locking + */ +static LIST_HEAD(cache_list); + +static struct cache_index_dir *kobj_to_cache_index_dir(struct kobject *k) +{ + return container_of(k, struct cache_index_dir, kobj); +} + +static const char *cache_type_string(const struct cache *cache) +{ + return cache_type_info[cache->type].name; +} + +static void __cpuinit cache_init(struct cache *cache, int type, int level, struct device_node *ofnode) +{ + cache->type = type; + cache->level = level; + cache->ofnode = of_node_get(ofnode); + INIT_LIST_HEAD(&cache->list); + list_add(&cache->list, &cache_list); +} + +static struct cache *__cpuinit new_cache(int type, int level, struct device_node *ofnode) +{ + struct cache *cache; + + cache = kzalloc(sizeof(*cache), GFP_KERNEL); + if (cache) + cache_init(cache, type, level, ofnode); + + return cache; +} + +static void release_cache_debugcheck(struct cache *cache) +{ + struct cache *iter; + + list_for_each_entry(iter, &cache_list, list) + WARN_ONCE(iter->next_local == cache, + "cache for %s(%s) refers to cache for %s(%s)\n", + iter->ofnode->full_name, + cache_type_string(iter), + cache->ofnode->full_name, + cache_type_string(cache)); +} + +static void release_cache(struct cache *cache) +{ + if (!cache) + return; + + pr_debug("freeing L%d %s cache for %s\n", cache->level, + cache_type_string(cache), cache->ofnode->full_name); + + release_cache_debugcheck(cache); + list_del(&cache->list); + of_node_put(cache->ofnode); + kfree(cache); +} + +static void cache_cpu_set(struct cache *cache, int cpu) +{ + struct cache *next = cache; + + while (next) { + WARN_ONCE(cpumask_test_cpu(cpu, &next->shared_cpu_map), + "CPU %i already accounted in %s(%s)\n", + cpu, next->ofnode->full_name, + cache_type_string(next)); + cpumask_set_cpu(cpu, &next->shared_cpu_map); + next = next->next_local; + } +} + +static int cache_size(const struct cache *cache, unsigned int *ret) +{ + const char *propname; + const u32 *cache_size; + + propname = cache_type_info[cache->type].size_prop; + + cache_size = of_get_property(cache->ofnode, propname, NULL); + if (!cache_size) + return -ENODEV; + + *ret = *cache_size; + return 0; +} + +static int cache_size_kb(const struct cache *cache, unsigned int *ret) +{ + unsigned int size; + + if (cache_size(cache, &size)) + return -ENODEV; + + *ret = size / 1024; + return 0; +} + +/* not cache_line_size() because that's a macro in include/linux/cache.h */ +static int cache_get_line_size(const struct cache *cache, unsigned int *ret) +{ + const u32 *line_size; + int i, lim; + + lim = ARRAY_SIZE(cache_type_info[cache->type].line_size_props); + + for (i = 0; i < lim; i++) { + const char *propname; + + propname = cache_type_info[cache->type].line_size_props[i]; + line_size = of_get_property(cache->ofnode, propname, NULL); + if (line_size) + break; + } + + if (!line_size) + return -ENODEV; + + *ret = *line_size; + return 0; +} + +static int cache_nr_sets(const struct cache *cache, unsigned int *ret) +{ + const char *propname; + const u32 *nr_sets; + + propname = cache_type_info[cache->type].nr_sets_prop; + + nr_sets = of_get_property(cache->ofnode, propname, NULL); + if (!nr_sets) + return -ENODEV; + + *ret = *nr_sets; + return 0; +} + +static int cache_associativity(const struct cache *cache, unsigned int *ret) +{ + unsigned int line_size; + unsigned int nr_sets; + unsigned int size; + + if (cache_nr_sets(cache, &nr_sets)) + goto err; + + /* If the cache is fully associative, there is no need to + * check the other properties. + */ + if (nr_sets == 1) { + *ret = 0; + return 0; + } + + if (cache_get_line_size(cache, &line_size)) + goto err; + if (cache_size(cache, &size)) + goto err; + + if (!(nr_sets > 0 && size > 0 && line_size > 0)) + goto err; + + *ret = (size / nr_sets) / line_size; + return 0; +err: + return -ENODEV; +} + +/* helper for dealing with split caches */ +static struct cache *cache_find_first_sibling(struct cache *cache) +{ + struct cache *iter; + + if (cache->type == CACHE_TYPE_UNIFIED) + return cache; + + list_for_each_entry(iter, &cache_list, list) + if (iter->ofnode == cache->ofnode && iter->next_local == cache) + return iter; + + return cache; +} + +/* return the first cache on a local list matching node */ +static struct cache *cache_lookup_by_node(const struct device_node *node) +{ + struct cache *cache = NULL; + struct cache *iter; + + list_for_each_entry(iter, &cache_list, list) { + if (iter->ofnode != node) + continue; + cache = cache_find_first_sibling(iter); + break; + } + + return cache; +} + +static bool cache_node_is_unified(const struct device_node *np) +{ + return of_get_property(np, "cache-unified", NULL); +} + +static struct cache *__cpuinit cache_do_one_devnode_unified(struct device_node *node, int level) +{ + struct cache *cache; + + pr_debug("creating L%d ucache for %s\n", level, node->full_name); + + cache = new_cache(CACHE_TYPE_UNIFIED, level, node); + + return cache; +} + +static struct cache *__cpuinit cache_do_one_devnode_split(struct device_node *node, int level) +{ + struct cache *dcache, *icache; + + pr_debug("creating L%d dcache and icache for %s\n", level, + node->full_name); + + dcache = new_cache(CACHE_TYPE_DATA, level, node); + icache = new_cache(CACHE_TYPE_INSTRUCTION, level, node); + + if (!dcache || !icache) + goto err; + + dcache->next_local = icache; + + return dcache; +err: + release_cache(dcache); + release_cache(icache); + return NULL; +} + +static struct cache *__cpuinit cache_do_one_devnode(struct device_node *node, int level) +{ + struct cache *cache; + + if (cache_node_is_unified(node)) + cache = cache_do_one_devnode_unified(node, level); + else + cache = cache_do_one_devnode_split(node, level); + + return cache; +} + +static struct cache *__cpuinit cache_lookup_or_instantiate(struct device_node *node, int level) +{ + struct cache *cache; + + cache = cache_lookup_by_node(node); + + WARN_ONCE(cache && cache->level != level, + "cache level mismatch on lookup (got %d, expected %d)\n", + cache->level, level); + + if (!cache) + cache = cache_do_one_devnode(node, level); + + return cache; +} + +static void __cpuinit link_cache_lists(struct cache *smaller, struct cache *bigger) +{ + while (smaller->next_local) { + if (smaller->next_local == bigger) + return; /* already linked */ + smaller = smaller->next_local; + } + + smaller->next_local = bigger; +} + +static void __cpuinit do_subsidiary_caches_debugcheck(struct cache *cache) +{ + WARN_ON_ONCE(cache->level != 1); + WARN_ON_ONCE(strcmp(cache->ofnode->type, "cpu")); +} + +static void __cpuinit do_subsidiary_caches(struct cache *cache) +{ + struct device_node *subcache_node; + int level = cache->level; + + do_subsidiary_caches_debugcheck(cache); + + while ((subcache_node = of_find_next_cache_node(cache->ofnode))) { + struct cache *subcache; + + level++; + subcache = cache_lookup_or_instantiate(subcache_node, level); + of_node_put(subcache_node); + if (!subcache) + break; + + link_cache_lists(cache, subcache); + cache = subcache; + } +} + +static struct cache *__cpuinit cache_chain_instantiate(unsigned int cpu_id) +{ + struct device_node *cpu_node; + struct cache *cpu_cache = NULL; + + pr_debug("creating cache object(s) for CPU %i\n", cpu_id); + + cpu_node = of_get_cpu_node(cpu_id, NULL); + WARN_ONCE(!cpu_node, "no OF node found for CPU %i\n", cpu_id); + if (!cpu_node) + goto out; + + cpu_cache = cache_lookup_or_instantiate(cpu_node, 1); + if (!cpu_cache) + goto out; + + do_subsidiary_caches(cpu_cache); + + cache_cpu_set(cpu_cache, cpu_id); +out: + of_node_put(cpu_node); + + return cpu_cache; +} + +static struct cache_dir *__cpuinit cacheinfo_create_cache_dir(unsigned int cpu_id) +{ + struct cache_dir *cache_dir; + struct sys_device *sysdev; + struct kobject *kobj = NULL; + + sysdev = get_cpu_sysdev(cpu_id); + WARN_ONCE(!sysdev, "no sysdev for CPU %i\n", cpu_id); + if (!sysdev) + goto err; + + kobj = kobject_create_and_add("cache", &sysdev->kobj); + if (!kobj) + goto err; + + cache_dir = kzalloc(sizeof(*cache_dir), GFP_KERNEL); + if (!cache_dir) + goto err; + + cache_dir->kobj = kobj; + + WARN_ON_ONCE(per_cpu(cache_dir, cpu_id) != NULL); + + per_cpu(cache_dir, cpu_id) = cache_dir; + + return cache_dir; +err: + kobject_put(kobj); + return NULL; +} + +static void cache_index_release(struct kobject *kobj) +{ + struct cache_index_dir *index; + + index = kobj_to_cache_index_dir(kobj); + + pr_debug("freeing index directory for L%d %s cache\n", + index->cache->level, cache_type_string(index->cache)); + + kfree(index); +} + +static ssize_t cache_index_show(struct kobject *k, struct attribute *attr, char *buf) +{ + struct kobj_attribute *kobj_attr; + + kobj_attr = container_of(attr, struct kobj_attribute, attr); + + return kobj_attr->show(k, kobj_attr, buf); +} + +static struct cache *index_kobj_to_cache(struct kobject *k) +{ + struct cache_index_dir *index; + + index = kobj_to_cache_index_dir(k); + + return index->cache; +} + +static ssize_t size_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + unsigned int size_kb; + struct cache *cache; + + cache = index_kobj_to_cache(k); + + if (cache_size_kb(cache, &size_kb)) + return -ENODEV; + + return sprintf(buf, "%uK\n", size_kb); +} + +static struct kobj_attribute cache_size_attr = + __ATTR(size, 0444, size_show, NULL); + + +static ssize_t line_size_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + unsigned int line_size; + struct cache *cache; + + cache = index_kobj_to_cache(k); + + if (cache_get_line_size(cache, &line_size)) + return -ENODEV; + + return sprintf(buf, "%u\n", line_size); +} + +static struct kobj_attribute cache_line_size_attr = + __ATTR(coherency_line_size, 0444, line_size_show, NULL); + +static ssize_t nr_sets_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + unsigned int nr_sets; + struct cache *cache; + + cache = index_kobj_to_cache(k); + + if (cache_nr_sets(cache, &nr_sets)) + return -ENODEV; + + return sprintf(buf, "%u\n", nr_sets); +} + +static struct kobj_attribute cache_nr_sets_attr = + __ATTR(number_of_sets, 0444, nr_sets_show, NULL); + +static ssize_t associativity_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + unsigned int associativity; + struct cache *cache; + + cache = index_kobj_to_cache(k); + + if (cache_associativity(cache, &associativity)) + return -ENODEV; + + return sprintf(buf, "%u\n", associativity); +} + +static struct kobj_attribute cache_assoc_attr = + __ATTR(ways_of_associativity, 0444, associativity_show, NULL); + +static ssize_t type_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + struct cache *cache; + + cache = index_kobj_to_cache(k); + + return sprintf(buf, "%s\n", cache_type_string(cache)); +} + +static struct kobj_attribute cache_type_attr = + __ATTR(type, 0444, type_show, NULL); + +static ssize_t level_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + struct cache_index_dir *index; + struct cache *cache; + + index = kobj_to_cache_index_dir(k); + cache = index->cache; + + return sprintf(buf, "%d\n", cache->level); +} + +static struct kobj_attribute cache_level_attr = + __ATTR(level, 0444, level_show, NULL); + +static ssize_t shared_cpu_map_show(struct kobject *k, struct kobj_attribute *attr, char *buf) +{ + struct cache_index_dir *index; + struct cache *cache; + int len; + int n = 0; + + index = kobj_to_cache_index_dir(k); + cache = index->cache; + len = PAGE_SIZE - 2; + + if (len > 1) { + n = cpumask_scnprintf(buf, len, &cache->shared_cpu_map); + buf[n++] = '\n'; + buf[n] = '\0'; + } + return n; +} + +static struct kobj_attribute cache_shared_cpu_map_attr = + __ATTR(shared_cpu_map, 0444, shared_cpu_map_show, NULL); + +/* Attributes which should always be created -- the kobject/sysfs core + * does this automatically via kobj_type->default_attrs. This is the + * minimum data required to uniquely identify a cache. + */ +static struct attribute *cache_index_default_attrs[] = { + &cache_type_attr.attr, + &cache_level_attr.attr, + &cache_shared_cpu_map_attr.attr, + NULL, +}; + +/* Attributes which should be created if the cache device node has the + * right properties -- see cacheinfo_create_index_opt_attrs + */ +static struct kobj_attribute *cache_index_opt_attrs[] = { + &cache_size_attr, + &cache_line_size_attr, + &cache_nr_sets_attr, + &cache_assoc_attr, +}; + +static struct sysfs_ops cache_index_ops = { + .show = cache_index_show, +}; + +static struct kobj_type cache_index_type = { + .release = cache_index_release, + .sysfs_ops = &cache_index_ops, + .default_attrs = cache_index_default_attrs, +}; + +static void __cpuinit cacheinfo_create_index_opt_attrs(struct cache_index_dir *dir) +{ + const char *cache_name; + const char *cache_type; + struct cache *cache; + char *buf; + int i; + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return; + + cache = dir->cache; + cache_name = cache->ofnode->full_name; + cache_type = cache_type_string(cache); + + /* We don't want to create an attribute that can't provide a + * meaningful value. Check the return value of each optional + * attribute's ->show method before registering the + * attribute. + */ + for (i = 0; i < ARRAY_SIZE(cache_index_opt_attrs); i++) { + struct kobj_attribute *attr; + ssize_t rc; + + attr = cache_index_opt_attrs[i]; + + rc = attr->show(&dir->kobj, attr, buf); + if (rc <= 0) { + pr_debug("not creating %s attribute for " + "%s(%s) (rc = %zd)\n", + attr->attr.name, cache_name, + cache_type, rc); + continue; + } + if (sysfs_create_file(&dir->kobj, &attr->attr)) + pr_debug("could not create %s attribute for %s(%s)\n", + attr->attr.name, cache_name, cache_type); + } + + kfree(buf); +} + +static void __cpuinit cacheinfo_create_index_dir(struct cache *cache, int index, struct cache_dir *cache_dir) +{ + struct cache_index_dir *index_dir; + int rc; + + index_dir = kzalloc(sizeof(*index_dir), GFP_KERNEL); + if (!index_dir) + goto err; + + index_dir->cache = cache; + + rc = kobject_init_and_add(&index_dir->kobj, &cache_index_type, + cache_dir->kobj, "index%d", index); + if (rc) + goto err; + + index_dir->next = cache_dir->index; + cache_dir->index = index_dir; + + cacheinfo_create_index_opt_attrs(index_dir); + + return; +err: + kfree(index_dir); +} + +static void __cpuinit cacheinfo_sysfs_populate(unsigned int cpu_id, struct cache *cache_list) +{ + struct cache_dir *cache_dir; + struct cache *cache; + int index = 0; + + cache_dir = cacheinfo_create_cache_dir(cpu_id); + if (!cache_dir) + return; + + cache = cache_list; + while (cache) { + cacheinfo_create_index_dir(cache, index, cache_dir); + index++; + cache = cache->next_local; + } +} + +void __cpuinit cacheinfo_cpu_online(unsigned int cpu_id) +{ + struct cache *cache; + + cache = cache_chain_instantiate(cpu_id); + if (!cache) + return; + + cacheinfo_sysfs_populate(cpu_id, cache); +} + +#ifdef CONFIG_HOTPLUG_CPU /* functions needed for cpu offline */ + +static struct cache *cache_lookup_by_cpu(unsigned int cpu_id) +{ + struct device_node *cpu_node; + struct cache *cache; + + cpu_node = of_get_cpu_node(cpu_id, NULL); + WARN_ONCE(!cpu_node, "no OF node found for CPU %i\n", cpu_id); + if (!cpu_node) + return NULL; + + cache = cache_lookup_by_node(cpu_node); + of_node_put(cpu_node); + + return cache; +} + +static void remove_index_dirs(struct cache_dir *cache_dir) +{ + struct cache_index_dir *index; + + index = cache_dir->index; + + while (index) { + struct cache_index_dir *next; + + next = index->next; + kobject_put(&index->kobj); + index = next; + } +} + +static void remove_cache_dir(struct cache_dir *cache_dir) +{ + remove_index_dirs(cache_dir); + + kobject_put(cache_dir->kobj); + + kfree(cache_dir); +} + +static void cache_cpu_clear(struct cache *cache, int cpu) +{ + while (cache) { + struct cache *next = cache->next_local; + + WARN_ONCE(!cpumask_test_cpu(cpu, &cache->shared_cpu_map), + "CPU %i not accounted in %s(%s)\n", + cpu, cache->ofnode->full_name, + cache_type_string(cache)); + + cpumask_clear_cpu(cpu, &cache->shared_cpu_map); + + /* Release the cache object if all the cpus using it + * are offline */ + if (cpumask_empty(&cache->shared_cpu_map)) + release_cache(cache); + + cache = next; + } +} + +void cacheinfo_cpu_offline(unsigned int cpu_id) +{ + struct cache_dir *cache_dir; + struct cache *cache; + + /* Prevent userspace from seeing inconsistent state - remove + * the sysfs hierarchy first */ + cache_dir = per_cpu(cache_dir, cpu_id); + + /* careful, sysfs population may have failed */ + if (cache_dir) + remove_cache_dir(cache_dir); + + per_cpu(cache_dir, cpu_id) = NULL; + + /* clear the CPU's bit in its cache chain, possibly freeing + * cache objects */ + cache = cache_lookup_by_cpu(cpu_id); + if (cache) + cache_cpu_clear(cache, cpu_id); +} +#endif /* CONFIG_HOTPLUG_CPU */ diff --git a/arch/powerpc/kernel/cacheinfo.h b/arch/powerpc/kernel/cacheinfo.h new file mode 100644 index 000000000000..a7b74d36acd7 --- /dev/null +++ b/arch/powerpc/kernel/cacheinfo.h @@ -0,0 +1,8 @@ +#ifndef _PPC_CACHEINFO_H +#define _PPC_CACHEINFO_H + +/* These are just hooks for sysfs.c to use. */ +extern void cacheinfo_cpu_online(unsigned int cpu_id); +extern void cacheinfo_cpu_offline(unsigned int cpu_id); + +#endif /* _PPC_CACHEINFO_H */ diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c index 0c64f10087b9..4a2ee08af6a7 100644 --- a/arch/powerpc/kernel/sysfs.c +++ b/arch/powerpc/kernel/sysfs.c @@ -18,6 +18,8 @@ #include #include +#include "cacheinfo.h" + #ifdef CONFIG_PPC64 #include #include @@ -25,8 +27,6 @@ static DEFINE_PER_CPU(struct cpu, cpu_devices); -static DEFINE_PER_CPU(struct kobject *, cache_toplevel); - /* * SMT snooze delay stuff, 64-bit only for now */ @@ -343,283 +343,6 @@ static struct sysdev_attribute pa6t_attrs[] = { #endif /* HAS_PPC_PMC_PA6T */ #endif /* HAS_PPC_PMC_CLASSIC */ -struct cache_desc { - struct kobject kobj; - struct cache_desc *next; - const char *type; /* Instruction, Data, or Unified */ - u32 size; /* total cache size in KB */ - u32 line_size; /* in bytes */ - u32 nr_sets; /* number of sets */ - u32 level; /* e.g. 1, 2, 3... */ - u32 associativity; /* e.g. 8-way... 0 is fully associative */ -}; - -DEFINE_PER_CPU(struct cache_desc *, cache_desc); - -static struct cache_desc *kobj_to_cache_desc(struct kobject *k) -{ - return container_of(k, struct cache_desc, kobj); -} - -static void cache_desc_release(struct kobject *k) -{ - struct cache_desc *desc = kobj_to_cache_desc(k); - - pr_debug("%s: releasing %s\n", __func__, kobject_name(k)); - - if (desc->next) - kobject_put(&desc->next->kobj); - - kfree(kobj_to_cache_desc(k)); -} - -static ssize_t cache_desc_show(struct kobject *k, struct attribute *attr, char *buf) -{ - struct kobj_attribute *kobj_attr; - - kobj_attr = container_of(attr, struct kobj_attribute, attr); - - return kobj_attr->show(k, kobj_attr, buf); -} - -static struct sysfs_ops cache_desc_sysfs_ops = { - .show = cache_desc_show, -}; - -static struct kobj_type cache_desc_type = { - .release = cache_desc_release, - .sysfs_ops = &cache_desc_sysfs_ops, -}; - -static ssize_t cache_size_show(struct kobject *k, struct kobj_attribute *attr, char *buf) -{ - struct cache_desc *cache = kobj_to_cache_desc(k); - - return sprintf(buf, "%uK\n", cache->size); -} - -static struct kobj_attribute cache_size_attr = - __ATTR(size, 0444, cache_size_show, NULL); - -static ssize_t cache_line_size_show(struct kobject *k, struct kobj_attribute *attr, char *buf) -{ - struct cache_desc *cache = kobj_to_cache_desc(k); - - return sprintf(buf, "%u\n", cache->line_size); -} - -static struct kobj_attribute cache_line_size_attr = - __ATTR(coherency_line_size, 0444, cache_line_size_show, NULL); - -static ssize_t cache_nr_sets_show(struct kobject *k, struct kobj_attribute *attr, char *buf) -{ - struct cache_desc *cache = kobj_to_cache_desc(k); - - return sprintf(buf, "%u\n", cache->nr_sets); -} - -static struct kobj_attribute cache_nr_sets_attr = - __ATTR(number_of_sets, 0444, cache_nr_sets_show, NULL); - -static ssize_t cache_type_show(struct kobject *k, struct kobj_attribute *attr, char *buf) -{ - struct cache_desc *cache = kobj_to_cache_desc(k); - - return sprintf(buf, "%s\n", cache->type); -} - -static struct kobj_attribute cache_type_attr = - __ATTR(type, 0444, cache_type_show, NULL); - -static ssize_t cache_level_show(struct kobject *k, struct kobj_attribute *attr, char *buf) -{ - struct cache_desc *cache = kobj_to_cache_desc(k); - - return sprintf(buf, "%u\n", cache->level); -} - -static struct kobj_attribute cache_level_attr = - __ATTR(level, 0444, cache_level_show, NULL); - -static ssize_t cache_assoc_show(struct kobject *k, struct kobj_attribute *attr, char *buf) -{ - struct cache_desc *cache = kobj_to_cache_desc(k); - - return sprintf(buf, "%u\n", cache->associativity); -} - -static struct kobj_attribute cache_assoc_attr = - __ATTR(ways_of_associativity, 0444, cache_assoc_show, NULL); - -struct cache_desc_info { - const char *type; - const char *size_prop; - const char *line_size_prop; - const char *nr_sets_prop; -}; - -/* PowerPC Processor binding says the [di]-cache-* must be equal on - * unified caches, so just use d-cache properties. */ -static struct cache_desc_info ucache_info = { - .type = "Unified", - .size_prop = "d-cache-size", - .line_size_prop = "d-cache-line-size", - .nr_sets_prop = "d-cache-sets", -}; - -static struct cache_desc_info dcache_info = { - .type = "Data", - .size_prop = "d-cache-size", - .line_size_prop = "d-cache-line-size", - .nr_sets_prop = "d-cache-sets", -}; - -static struct cache_desc_info icache_info = { - .type = "Instruction", - .size_prop = "i-cache-size", - .line_size_prop = "i-cache-line-size", - .nr_sets_prop = "i-cache-sets", -}; - -static struct cache_desc * __cpuinit create_cache_desc(struct device_node *np, struct kobject *parent, int index, int level, struct cache_desc_info *info) -{ - const u32 *cache_line_size; - struct cache_desc *new; - const u32 *cache_size; - const u32 *nr_sets; - int rc; - - new = kzalloc(sizeof(*new), GFP_KERNEL); - if (!new) - return NULL; - - rc = kobject_init_and_add(&new->kobj, &cache_desc_type, parent, - "index%d", index); - if (rc) - goto err; - - /* type */ - new->type = info->type; - rc = sysfs_create_file(&new->kobj, &cache_type_attr.attr); - WARN_ON(rc); - - /* level */ - new->level = level; - rc = sysfs_create_file(&new->kobj, &cache_level_attr.attr); - WARN_ON(rc); - - /* size */ - cache_size = of_get_property(np, info->size_prop, NULL); - if (cache_size) { - new->size = *cache_size / 1024; - rc = sysfs_create_file(&new->kobj, - &cache_size_attr.attr); - WARN_ON(rc); - } - - /* coherency_line_size */ - cache_line_size = of_get_property(np, info->line_size_prop, NULL); - if (cache_line_size) { - new->line_size = *cache_line_size; - rc = sysfs_create_file(&new->kobj, - &cache_line_size_attr.attr); - WARN_ON(rc); - } - - /* number_of_sets */ - nr_sets = of_get_property(np, info->nr_sets_prop, NULL); - if (nr_sets) { - new->nr_sets = *nr_sets; - rc = sysfs_create_file(&new->kobj, - &cache_nr_sets_attr.attr); - WARN_ON(rc); - } - - /* ways_of_associativity */ - if (new->nr_sets == 1) { - /* fully associative */ - new->associativity = 0; - goto create_assoc; - } - - if (new->nr_sets && new->size && new->line_size) { - /* If we have values for all of these we can derive - * the associativity. */ - new->associativity = - ((new->size * 1024) / new->nr_sets) / new->line_size; -create_assoc: - rc = sysfs_create_file(&new->kobj, - &cache_assoc_attr.attr); - WARN_ON(rc); - } - - return new; -err: - kfree(new); - return NULL; -} - -static bool cache_is_unified(struct device_node *np) -{ - return of_get_property(np, "cache-unified", NULL); -} - -static struct cache_desc * __cpuinit create_cache_index_info(struct device_node *np, struct kobject *parent, int index, int level) -{ - struct device_node *next_cache; - struct cache_desc *new, **end; - - pr_debug("%s(node = %s, index = %d)\n", __func__, np->full_name, index); - - if (cache_is_unified(np)) { - new = create_cache_desc(np, parent, index, level, - &ucache_info); - } else { - new = create_cache_desc(np, parent, index, level, - &dcache_info); - if (new) { - index++; - new->next = create_cache_desc(np, parent, index, level, - &icache_info); - } - } - if (!new) - return NULL; - - end = &new->next; - while (*end) - end = &(*end)->next; - - next_cache = of_find_next_cache_node(np); - if (!next_cache) - goto out; - - *end = create_cache_index_info(next_cache, parent, ++index, ++level); - - of_node_put(next_cache); -out: - return new; -} - -static void __cpuinit create_cache_info(struct sys_device *sysdev) -{ - struct kobject *cache_toplevel; - struct device_node *np = NULL; - int cpu = sysdev->id; - - cache_toplevel = kobject_create_and_add("cache", &sysdev->kobj); - if (!cache_toplevel) - return; - per_cpu(cache_toplevel, cpu) = cache_toplevel; - np = of_get_cpu_node(cpu, NULL); - if (np != NULL) { - per_cpu(cache_desc, cpu) = - create_cache_index_info(np, cache_toplevel, 0, 1); - of_node_put(np); - } - return; -} - static void __cpuinit register_cpu_online(unsigned int cpu) { struct cpu *c = &per_cpu(cpu_devices, cpu); @@ -684,25 +407,10 @@ static void __cpuinit register_cpu_online(unsigned int cpu) sysdev_create_file(s, &attr_dscr); #endif /* CONFIG_PPC64 */ - create_cache_info(s); + cacheinfo_cpu_online(cpu); } #ifdef CONFIG_HOTPLUG_CPU -static void remove_cache_info(struct sys_device *sysdev) -{ - struct kobject *cache_toplevel; - struct cache_desc *cache_desc; - int cpu = sysdev->id; - - cache_desc = per_cpu(cache_desc, cpu); - if (cache_desc != NULL) - kobject_put(&cache_desc->kobj); - - cache_toplevel = per_cpu(cache_toplevel, cpu); - if (cache_toplevel != NULL) - kobject_put(cache_toplevel); -} - static void unregister_cpu_online(unsigned int cpu) { struct cpu *c = &per_cpu(cpu_devices, cpu); @@ -769,7 +477,7 @@ static void unregister_cpu_online(unsigned int cpu) sysdev_remove_file(s, &attr_dscr); #endif /* CONFIG_PPC64 */ - remove_cache_info(s); + cacheinfo_cpu_offline(cpu); } #endif /* CONFIG_HOTPLUG_CPU */ -- cgit v1.2.3 From 2701a1ad04ebab0c8fae8beb4586383a5a70ba0c Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 25 Dec 2008 04:33:34 +0000 Subject: powerpc/52xx: Use DEFINE_SPINLOCK SPIN_LOCK_UNLOCKED is deprecated. The following makes the change suggested in Documentation/spinlocks.txt The semantic patch that makes this change is as follows: (http://www.emn.fr/x-info/coccinelle/) // @@ declarer name DEFINE_SPINLOCK; identifier xxx_lock; @@ - spinlock_t xxx_lock = SPIN_LOCK_UNLOCKED; + DEFINE_SPINLOCK(xxx_lock); // Signed-off-by: Julia Lawall Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/52xx/mpc52xx_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/52xx/mpc52xx_common.c b/arch/powerpc/platforms/52xx/mpc52xx_common.c index ae7c34f37e1c..98367a0255f3 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_common.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_common.c @@ -42,7 +42,7 @@ static struct of_device_id mpc52xx_bus_ids[] __initdata = { * from interrupt context while node mapping (which calls ioremap()) * cannot be used at such point. */ -static spinlock_t mpc52xx_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(mpc52xx_lock); static struct mpc52xx_gpt __iomem *mpc52xx_wdt; static struct mpc52xx_cdm __iomem *mpc52xx_cdm; -- cgit v1.2.3 From 1d5bc03a8183d12c7daf4e7c69cce8d9c4b9a86b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 25 Dec 2008 04:34:04 +0000 Subject: powerpc/pasemi: Use DEFINE_SPINLOCK SPIN_LOCK_UNLOCKED is deprecated. The following makes the change suggested in Documentation/spinlocks.txt The semantic patch that makes this change is as follows: (http://www.emn.fr/x-info/coccinelle/) // @@ declarer name DEFINE_SPINLOCK; identifier xxx_lock; @@ - spinlock_t xxx_lock = SPIN_LOCK_UNLOCKED; + DEFINE_SPINLOCK(xxx_lock); // Signed-off-by: Julia Lawall Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pasemi/dma_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pasemi/dma_lib.c b/arch/powerpc/platforms/pasemi/dma_lib.c index 217af321b0ca..a6152d922243 100644 --- a/arch/powerpc/platforms/pasemi/dma_lib.c +++ b/arch/powerpc/platforms/pasemi/dma_lib.c @@ -509,7 +509,7 @@ fallback: */ int pasemi_dma_init(void) { - static spinlock_t init_lock = SPIN_LOCK_UNLOCKED; + static DEFINE_SPINLOCK(init_lock); struct pci_dev *iob_pdev; struct pci_dev *pdev; struct resource res; -- cgit v1.2.3 From 16124f10df43e6e08783f1fede6888bf36ac705c Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 28 Dec 2008 14:12:57 +0000 Subject: powerpc: Fix pciconfig_iobase system call on PCI-Express powermac X has been failing to start on my quad G5 powermac since commit 1fd0f52583a85b21a394201b007bc1ee104b235d ("powerpc: Fix domain numbers in /proc on 64-bit") went in. The reason is that the change allows X to see the PCI-PCI bridge above the video card (previously it was obscured by the fact that there were two "00" directories in /proc/bus/pci), and the pciconfig_iobase system call on the bridge is failing because of a hack that we have to return information about the AGP bus when X asks about bus 0. This machine doesn't have an AGP bus (it has PCI Express) and so the pciconfig_iobase call is returning -1, which ultimately causes X to fail to start. This fixes it by checking that we have an AGP bridge before redirecting the pciconfig_iobase call to return information about the AGP bus. With this, X starts successfully both on a quad G5 with PCI Express and on an older dual G5 with AGP. Signed-off-by: Paul Mackerras Acked-by: Benjamin Herrenschmidt Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/pci_64.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 39fadc6e1492..586962f65c2a 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -560,9 +560,14 @@ long sys_pciconfig_iobase(long which, unsigned long in_bus, * G5 machines... So when something asks for bus 0 io base * (bus 0 is HT root), we return the AGP one instead. */ - if (machine_is_compatible("MacRISC4")) - if (in_bus == 0) + if (in_bus == 0 && machine_is_compatible("MacRISC4")) { + struct device_node *agp; + + agp = of_find_compatible_node(NULL, NULL, "u3-agp"); + if (agp) in_bus = 0xf0; + of_node_put(agp); + } /* That syscall isn't quite compatible with PCI domains, but it's * used on pre-domains setup. We return the first match -- cgit v1.2.3 From d50701781a55d09696a0585112a124b0723acf3b Mon Sep 17 00:00:00 2001 From: Matthias Fuchs Date: Tue, 30 Dec 2008 00:59:38 +0000 Subject: powerpc: Add ioctls for RS485 mode control of serial drivers These ioctls take a struct serial_rs485 (see linux/serial.h) as argument. They are already available on x86. This patch adds them for the powerpc architecture. Signed-off-by: Matthias Fuchs Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/ioctls.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/include/asm/ioctls.h b/arch/powerpc/include/asm/ioctls.h index 279a6229584b..1842186d872c 100644 --- a/arch/powerpc/include/asm/ioctls.h +++ b/arch/powerpc/include/asm/ioctls.h @@ -89,6 +89,8 @@ #define TIOCSBRK 0x5427 /* BSD compatibility */ #define TIOCCBRK 0x5428 /* BSD compatibility */ #define TIOCGSID 0x5429 /* Return the session ID of FD */ +#define TIOCGRS485 0x542e +#define TIOCSRS485 0x542f #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ -- cgit v1.2.3 From ee418b8646d5d6dc2df343ffaefd2b74940d0048 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 30 Dec 2008 17:06:15 +0000 Subject: powerpc/cell: Bitops work on unsigned longs So change the flags member of struct spu from u64 to unsigned long. This change will also prevent some warnings when we change u64 to unsigned long long. Signed-off-by: Stephen Rothwell Acked-by: Arnd Bergmann Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/spu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/spu.h b/arch/powerpc/include/asm/spu.h index 8b2eb044270a..0ab8d869e3d6 100644 --- a/arch/powerpc/include/asm/spu.h +++ b/arch/powerpc/include/asm/spu.h @@ -128,7 +128,7 @@ struct spu { int number; unsigned int irqs[3]; u32 node; - u64 flags; + unsigned long flags; u64 class_0_pending; u64 class_0_dar; u64 class_1_dar; -- cgit v1.2.3 From 54cfd0d5cac9df4f7b32969156a55f7e0d9297ad Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 30 Dec 2008 17:09:15 +0000 Subject: powerpc/cell: local_irq_save takes an unsigned long Signed-off-by: Stephen Rothwell Acked-by: Arnd Bergmann Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/cell/cbe_cpufreq_pervasive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq_pervasive.c b/arch/powerpc/platforms/cell/cbe_cpufreq_pervasive.c index 70fa7aef5edd..20472e487b6f 100644 --- a/arch/powerpc/platforms/cell/cbe_cpufreq_pervasive.c +++ b/arch/powerpc/platforms/cell/cbe_cpufreq_pervasive.c @@ -54,7 +54,7 @@ int cbe_cpufreq_set_pmode(int cpu, unsigned int pmode) { struct cbe_pmd_regs __iomem *pmd_regs; struct cbe_mic_tm_regs __iomem *mic_tm_regs; - u64 flags; + unsigned long flags; u64 value; #ifdef DEBUG long time; -- cgit v1.2.3 From d6a09e0cd6329db6f48b0015407a1b038d8fa64b Mon Sep 17 00:00:00 2001 From: Dave Liu Date: Tue, 30 Dec 2008 23:42:55 +0000 Subject: powerpc: Remove the redundant _tlbil_pid at SMP case Signed-off-by: Dave Liu Acked-by: Benjamin Herrenschmidt Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/tlb_nohash.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/mm/tlb_nohash.c b/arch/powerpc/mm/tlb_nohash.c index 803a64c02b06..39ac22b13c73 100644 --- a/arch/powerpc/mm/tlb_nohash.c +++ b/arch/powerpc/mm/tlb_nohash.c @@ -189,8 +189,9 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end) smp_call_function(do_flush_tlb_mm_ipi, NULL, 1); _tlbil_pid(0); preempt_enable(); -#endif +#else _tlbil_pid(0); +#endif } EXPORT_SYMBOL(flush_tlb_kernel_range); -- cgit v1.2.3 From c6ac71a14aec8278507a71d9d9f496dc9adad010 Mon Sep 17 00:00:00 2001 From: Mohan Kumar M Date: Tue, 6 Jan 2009 00:23:01 +0000 Subject: powerpc: Enable RELOCATABLE option for CRASH_DUMP Enable RELOCATABLE option if user selects CRASH_DUMP option. Without this patch user has to first select RELOCATABLE option and then has to enable CRASH_DUMP option. Signed-off-by: M. Mohan Kumar Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 79f25cef32df..acda55199f55 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -326,7 +326,8 @@ config KEXEC config CRASH_DUMP bool "Build a kdump crash kernel" - depends on (PPC64 && RELOCATABLE) || 6xx + depends on PPC64 || 6xx + select RELOCATABLE if PPC64 help Build a kernel suitable for use as a kdump capture kernel. The same kernel binary can be used as production kernel and dump -- cgit v1.2.3 From d2b4397bf87cf6547ca9fa75b6b84eada96c0848 Mon Sep 17 00:00:00 2001 From: Kamalesh Babulal Date: Tue, 6 Jan 2009 05:57:24 +0000 Subject: powerpc: Fix iseries drivers build failure without CONFIG_VIOPATH iSeries dependent drivers fail to build, when CONFIG_VIOPATH is disabled. Fix the problem by making those drivers select it. Signed-off-by: Kamalesh Babulal Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/iseries/Kconfig | 5 +++-- drivers/char/Kconfig | 1 + drivers/scsi/Kconfig | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/iseries/Kconfig b/arch/powerpc/platforms/iseries/Kconfig index ed3753d8c109..7ddd0a2c8027 100644 --- a/arch/powerpc/platforms/iseries/Kconfig +++ b/arch/powerpc/platforms/iseries/Kconfig @@ -10,18 +10,21 @@ menu "iSeries device drivers" config VIODASD tristate "iSeries Virtual I/O disk support" depends on BLOCK + select VIOPATH help If you are running on an iSeries system and you want to use virtual disks created and managed by OS/400, say Y. config VIOCD tristate "iSeries Virtual I/O CD support" + select VIOPATH help If you are running Linux on an IBM iSeries system and you want to read a CD drive owned by OS/400, say Y here. config VIOTAPE tristate "iSeries Virtual Tape Support" + select VIOPATH help If you are running Linux on an iSeries system and you want Linux to read and/or write a tape drive owned by OS/400, say Y here. @@ -30,5 +33,3 @@ endmenu config VIOPATH bool - depends on VIODASD || VIOCD || VIOTAPE || ISERIES_VETH - default y diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 35914b6e1d2a..f5be8081cd81 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -616,6 +616,7 @@ config HVC_ISERIES default y select HVC_DRIVER select HVC_IRQ + select VIOPATH help iSeries machines support a hypervisor virtual console. diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index b7322976d2b7..256c7bec7bd7 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -884,6 +884,7 @@ config SCSI_IBMVSCSI tristate "IBM Virtual SCSI support" depends on PPC_PSERIES || PPC_ISERIES select SCSI_SRP_ATTRS + select VIOPATH if PPC_ISERIES help This is the IBM POWER Virtual SCSI Client -- cgit v1.2.3 From cffb4add03b1fc83026b06dc3664279cfbf70155 Mon Sep 17 00:00:00 2001 From: Jim Paris Date: Tue, 6 Jan 2009 11:32:10 +0000 Subject: mtd/ps3vram: Add ps3vram driver for accessing video RAM as MTD Add ps3vram driver, which exposes unused video RAM on the PS3 as a MTD device suitable for storage or swap. Fast data transfer is achieved using a local cache in system RAM and DMA transfers via the GPU. Signed-off-by: Vivien Chappelier Signed-off-by: Jim Paris Acked-by: Geoff Levand Acked-by: David Woodhouse Signed-off-by: Benjamin Herrenschmidt --- MAINTAINERS | 6 + arch/powerpc/include/asm/ps3.h | 1 + arch/powerpc/platforms/ps3/device-init.c | 37 ++ drivers/mtd/devices/Kconfig | 7 + drivers/mtd/devices/Makefile | 1 + drivers/mtd/devices/ps3vram.c | 776 +++++++++++++++++++++++++++++++ 6 files changed, 828 insertions(+) create mode 100644 drivers/mtd/devices/ps3vram.c diff --git a/MAINTAINERS b/MAINTAINERS index a01884407fe4..246878f41cf5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3484,6 +3484,12 @@ L: linuxppc-dev@ozlabs.org L: cbe-oss-dev@ozlabs.org S: Supported +PS3VRAM DRIVER +P: Jim Paris +M: jim@jtan.com +L: cbe-oss-dev@ozlabs.org +S: Maintained + PVRUSB2 VIDEO4LINUX DRIVER P: Mike Isely M: isely@pobox.com diff --git a/arch/powerpc/include/asm/ps3.h b/arch/powerpc/include/asm/ps3.h index cff30c0ef1ff..66b650532adf 100644 --- a/arch/powerpc/include/asm/ps3.h +++ b/arch/powerpc/include/asm/ps3.h @@ -320,6 +320,7 @@ enum ps3_match_id { enum ps3_match_sub_id { PS3_MATCH_SUB_ID_GPU_FB = 1, + PS3_MATCH_SUB_ID_GPU_RAMDISK = 2, }; #define PS3_MODULE_ALIAS_EHCI "ps3:1:0" diff --git a/arch/powerpc/platforms/ps3/device-init.c b/arch/powerpc/platforms/ps3/device-init.c index dbc124e05646..ca71a12b764c 100644 --- a/arch/powerpc/platforms/ps3/device-init.c +++ b/arch/powerpc/platforms/ps3/device-init.c @@ -518,6 +518,41 @@ fail_device_register: return result; } +static int __init ps3_register_ramdisk_device(void) +{ + int result; + struct layout { + struct ps3_system_bus_device dev; + } *p; + + pr_debug(" -> %s:%d\n", __func__, __LINE__); + + p = kzalloc(sizeof(struct layout), GFP_KERNEL); + + if (!p) + return -ENOMEM; + + p->dev.match_id = PS3_MATCH_ID_GPU; + p->dev.match_sub_id = PS3_MATCH_SUB_ID_GPU_RAMDISK; + p->dev.dev_type = PS3_DEVICE_TYPE_IOC0; + + result = ps3_system_bus_device_register(&p->dev); + + if (result) { + pr_debug("%s:%d ps3_system_bus_device_register failed\n", + __func__, __LINE__); + goto fail_device_register; + } + + pr_debug(" <- %s:%d\n", __func__, __LINE__); + return 0; + +fail_device_register: + kfree(p); + pr_debug(" <- %s:%d failed\n", __func__, __LINE__); + return result; +} + /** * ps3_setup_dynamic_device - Setup a dynamic device from the repository */ @@ -946,6 +981,8 @@ static int __init ps3_register_devices(void) ps3_register_lpm_devices(); + ps3_register_ramdisk_device(); + pr_debug(" <- %s:%d\n", __func__, __LINE__); return 0; } diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 6fde0a2e3567..bc33200535fc 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -120,6 +120,13 @@ config MTD_PHRAM doesn't have access to, memory beyond the mem=xxx limit, nvram, memory on the video card, etc... +config MTD_PS3VRAM + tristate "PS3 video RAM" + depends on FB_PS3 + help + This driver allows you to use excess PS3 video RAM as volatile + storage or system swap. + config MTD_LART tristate "28F160xx flash driver for LART" depends on SA1100_LART diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 0993d5cf3923..e51521df4e40 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o obj-$(CONFIG_MTD_M25P80) += m25p80.o +obj-$(CONFIG_MTD_PS3VRAM) += ps3vram.o diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c new file mode 100644 index 000000000000..26a4b57662f6 --- /dev/null +++ b/drivers/mtd/devices/ps3vram.c @@ -0,0 +1,776 @@ +/** + * ps3vram - Use extra PS3 video ram as MTD block device. + * + * Copyright (c) 2007-2008 Jim Paris + * Added support RSX DMA Vivien Chappelier + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DEVICE_NAME "ps3vram" + +#define XDR_BUF_SIZE (2 * 1024 * 1024) /* XDR buffer (must be 1MiB aligned) */ +#define XDR_IOIF 0x0c000000 + +#define FIFO_BASE XDR_IOIF +#define FIFO_SIZE (64 * 1024) + +#define DMA_PAGE_SIZE (4 * 1024) + +#define CACHE_PAGE_SIZE (256 * 1024) +#define CACHE_PAGE_COUNT ((XDR_BUF_SIZE - FIFO_SIZE) / CACHE_PAGE_SIZE) + +#define CACHE_OFFSET CACHE_PAGE_SIZE +#define FIFO_OFFSET 0 + +#define CTRL_PUT 0x10 +#define CTRL_GET 0x11 +#define CTRL_TOP 0x15 + +#define UPLOAD_SUBCH 1 +#define DOWNLOAD_SUBCH 2 + +#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c +#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104 + +#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601 + +struct mtd_info ps3vram_mtd; + +#define CACHE_PAGE_PRESENT 1 +#define CACHE_PAGE_DIRTY 2 + +#define dbg(fmt, args...) \ + pr_debug("%s:%d " fmt "\n", __func__, __LINE__, ## args) + +struct ps3vram_tag { + unsigned int address; + unsigned int flags; +}; + +struct ps3vram_cache { + unsigned int page_count; + unsigned int page_size; + struct ps3vram_tag *tags; +}; + +struct ps3vram_priv { + uint64_t memory_handle; + uint64_t context_handle; + uint8_t *base; + uint32_t *ctrl; + uint32_t *reports; + uint8_t *xdr_buf; + + uint32_t *fifo_base; + uint32_t *fifo_ptr; + + struct ps3vram_cache cache; + + /* Used to serialize cache/DMA operations */ + struct mutex lock; +}; + +#define DMA_NOTIFIER_HANDLE_BASE 0x66604200 /* first DMA notifier handle */ +#define DMA_NOTIFIER_OFFSET_BASE 0x1000 /* first DMA notifier offset */ +#define DMA_NOTIFIER_SIZE 0x40 + +#define NUM_NOTIFIERS 16 + +#define NOTIFIER 7 /* notifier used for completion report */ + +/* A trailing '-' means to subtract off ps3fb_videomemory.size */ +char *size = "256M-"; +module_param(size, charp, 0); +MODULE_PARM_DESC(size, "memory size"); + +static inline uint32_t *ps3vram_get_notifier(uint32_t *reports, int notifier) +{ + return (void *) reports + + DMA_NOTIFIER_OFFSET_BASE + + DMA_NOTIFIER_SIZE * notifier; +} + +static void ps3vram_notifier_reset(struct mtd_info *mtd) +{ + int i; + struct ps3vram_priv *priv = mtd->priv; + uint32_t *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); + for (i = 0; i < 4; i++) + notify[i] = 0xffffffff; +} + +static int ps3vram_notifier_wait(struct mtd_info *mtd, int timeout_ms) +{ + struct ps3vram_priv *priv = mtd->priv; + uint32_t *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); + + timeout_ms *= 1000; + + do { + if (notify[3] == 0) + return 0; + + if (timeout_ms) + udelay(1); + } while (timeout_ms--); + + return -1; +} + +static void ps3vram_dump_ring(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + uint32_t *fifo; + + pr_info("PUT = %08x GET = %08x\n", priv->ctrl[CTRL_PUT], + priv->ctrl[CTRL_GET]); + for (fifo = priv->fifo_base; fifo < priv->fifo_ptr; fifo++) + pr_info("%p: %08x\n", fifo, *fifo); +} + +static void ps3vram_dump_reports(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + int i; + + for (i = 0; i < NUM_NOTIFIERS; i++) { + uint32_t *n = ps3vram_get_notifier(priv->reports, i); + pr_info("%p: %08x\n", n, *n); + } +} + +static void ps3vram_init_ring(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + + priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET; + priv->ctrl[CTRL_GET] = FIFO_BASE + FIFO_OFFSET; +} + +static int ps3vram_wait_ring(struct mtd_info *mtd, int timeout) +{ + struct ps3vram_priv *priv = mtd->priv; + + /* wait until setup commands are processed */ + timeout *= 1000; + while (--timeout) { + if (priv->ctrl[CTRL_PUT] == priv->ctrl[CTRL_GET]) + break; + udelay(1); + } + if (timeout == 0) { + pr_err("FIFO timeout (%08x/%08x/%08x)\n", priv->ctrl[CTRL_PUT], + priv->ctrl[CTRL_GET], priv->ctrl[CTRL_TOP]); + return -ETIMEDOUT; + } + + return 0; +} + +static inline void ps3vram_out_ring(struct ps3vram_priv *priv, uint32_t data) +{ + *(priv->fifo_ptr)++ = data; +} + +static inline void ps3vram_begin_ring(struct ps3vram_priv *priv, uint32_t chan, + uint32_t tag, uint32_t size) +{ + ps3vram_out_ring(priv, (size << 18) | (chan << 13) | tag); +} + +static void ps3vram_rewind_ring(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + u64 status; + + ps3vram_out_ring(priv, 0x20000000 | (FIFO_BASE + FIFO_OFFSET)); + + priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET; + + /* asking the HV for a blit will kick the fifo */ + status = lv1_gpu_context_attribute(priv->context_handle, + L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, + 0, 0, 0, 0); + if (status) + pr_err("ps3vram: lv1_gpu_context_attribute FB_BLIT failed\n"); + + priv->fifo_ptr = priv->fifo_base; +} + +static void ps3vram_fire_ring(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + u64 status; + + mutex_lock(&ps3_gpu_mutex); + + priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET + + (priv->fifo_ptr - priv->fifo_base) * sizeof(uint32_t); + + /* asking the HV for a blit will kick the fifo */ + status = lv1_gpu_context_attribute(priv->context_handle, + L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, + 0, 0, 0, 0); + if (status) + pr_err("ps3vram: lv1_gpu_context_attribute FB_BLIT failed\n"); + + if ((priv->fifo_ptr - priv->fifo_base) * sizeof(uint32_t) > + FIFO_SIZE - 1024) { + dbg("fifo full, rewinding"); + ps3vram_wait_ring(mtd, 200); + ps3vram_rewind_ring(mtd); + } + + mutex_unlock(&ps3_gpu_mutex); +} + +static void ps3vram_bind(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + + ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0, 1); + ps3vram_out_ring(priv, 0x31337303); + ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x180, 3); + ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER); + ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */ + ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */ + + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0, 1); + ps3vram_out_ring(priv, 0x3137c0de); + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x180, 3); + ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER); + ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */ + ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */ + + ps3vram_fire_ring(mtd); +} + +static int ps3vram_upload(struct mtd_info *mtd, unsigned int src_offset, + unsigned int dst_offset, int len, int count) +{ + struct ps3vram_priv *priv = mtd->priv; + + ps3vram_begin_ring(priv, UPLOAD_SUBCH, + NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8); + ps3vram_out_ring(priv, XDR_IOIF + src_offset); + ps3vram_out_ring(priv, dst_offset); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, count); + ps3vram_out_ring(priv, (1 << 8) | 1); + ps3vram_out_ring(priv, 0); + + ps3vram_notifier_reset(mtd); + ps3vram_begin_ring(priv, UPLOAD_SUBCH, + NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1); + ps3vram_out_ring(priv, 0); + ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x100, 1); + ps3vram_out_ring(priv, 0); + ps3vram_fire_ring(mtd); + if (ps3vram_notifier_wait(mtd, 200) < 0) { + pr_err("notifier timeout\n"); + ps3vram_dump_ring(mtd); + ps3vram_dump_reports(mtd); + return -1; + } + + return 0; +} + +static int ps3vram_download(struct mtd_info *mtd, unsigned int src_offset, + unsigned int dst_offset, int len, int count) +{ + struct ps3vram_priv *priv = mtd->priv; + + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, + NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8); + ps3vram_out_ring(priv, src_offset); + ps3vram_out_ring(priv, XDR_IOIF + dst_offset); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, count); + ps3vram_out_ring(priv, (1 << 8) | 1); + ps3vram_out_ring(priv, 0); + + ps3vram_notifier_reset(mtd); + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, + NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1); + ps3vram_out_ring(priv, 0); + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x100, 1); + ps3vram_out_ring(priv, 0); + ps3vram_fire_ring(mtd); + if (ps3vram_notifier_wait(mtd, 200) < 0) { + pr_err("notifier timeout\n"); + ps3vram_dump_ring(mtd); + ps3vram_dump_reports(mtd); + return -1; + } + + return 0; +} + +static void ps3vram_cache_evict(struct mtd_info *mtd, int entry) +{ + struct ps3vram_priv *priv = mtd->priv; + struct ps3vram_cache *cache = &priv->cache; + + if (cache->tags[entry].flags & CACHE_PAGE_DIRTY) { + dbg("flushing %d : 0x%08x", entry, cache->tags[entry].address); + if (ps3vram_upload(mtd, + CACHE_OFFSET + entry * cache->page_size, + cache->tags[entry].address, + DMA_PAGE_SIZE, + cache->page_size / DMA_PAGE_SIZE) < 0) { + pr_err("failed to upload from 0x%x to 0x%x size 0x%x\n", + entry * cache->page_size, + cache->tags[entry].address, + cache->page_size); + } + cache->tags[entry].flags &= ~CACHE_PAGE_DIRTY; + } +} + +static void ps3vram_cache_load(struct mtd_info *mtd, int entry, + unsigned int address) +{ + struct ps3vram_priv *priv = mtd->priv; + struct ps3vram_cache *cache = &priv->cache; + + dbg("fetching %d : 0x%08x", entry, address); + if (ps3vram_download(mtd, + address, + CACHE_OFFSET + entry * cache->page_size, + DMA_PAGE_SIZE, + cache->page_size / DMA_PAGE_SIZE) < 0) { + pr_err("failed to download from 0x%x to 0x%x size 0x%x\n", + address, + entry * cache->page_size, + cache->page_size); + } + + cache->tags[entry].address = address; + cache->tags[entry].flags |= CACHE_PAGE_PRESENT; +} + + +static void ps3vram_cache_flush(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + struct ps3vram_cache *cache = &priv->cache; + int i; + + dbg("FLUSH"); + for (i = 0; i < cache->page_count; i++) { + ps3vram_cache_evict(mtd, i); + cache->tags[i].flags = 0; + } +} + +static unsigned int ps3vram_cache_match(struct mtd_info *mtd, loff_t address) +{ + struct ps3vram_priv *priv = mtd->priv; + struct ps3vram_cache *cache = &priv->cache; + unsigned int base; + unsigned int offset; + int i; + static int counter; + + offset = (unsigned int) (address & (cache->page_size - 1)); + base = (unsigned int) (address - offset); + + /* fully associative check */ + for (i = 0; i < cache->page_count; i++) { + if ((cache->tags[i].flags & CACHE_PAGE_PRESENT) && + cache->tags[i].address == base) { + dbg("found entry %d : 0x%08x", + i, cache->tags[i].address); + return i; + } + } + + /* choose a random entry */ + i = (jiffies + (counter++)) % cache->page_count; + dbg("using cache entry %d", i); + + ps3vram_cache_evict(mtd, i); + ps3vram_cache_load(mtd, i, base); + + return i; +} + +static int ps3vram_cache_init(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + + pr_info("creating cache: %d entries, %d bytes pages\n", + CACHE_PAGE_COUNT, CACHE_PAGE_SIZE); + + priv->cache.page_count = CACHE_PAGE_COUNT; + priv->cache.page_size = CACHE_PAGE_SIZE; + priv->cache.tags = kzalloc(sizeof(struct ps3vram_tag) * + CACHE_PAGE_COUNT, GFP_KERNEL); + if (priv->cache.tags == NULL) { + pr_err("could not allocate cache tags\n"); + return -ENOMEM; + } + + return 0; +} + +static void ps3vram_cache_cleanup(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + + ps3vram_cache_flush(mtd); + kfree(priv->cache.tags); +} + +static int ps3vram_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct ps3vram_priv *priv = mtd->priv; + + if (instr->addr + instr->len > mtd->size) + return -EINVAL; + + mutex_lock(&priv->lock); + + ps3vram_cache_flush(mtd); + + /* Set bytes to 0xFF */ + memset(priv->base + instr->addr, 0xFF, instr->len); + + mutex_unlock(&priv->lock); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} + + +static int ps3vram_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct ps3vram_priv *priv = mtd->priv; + unsigned int cached, count; + + dbg("from = 0x%08x len = 0x%zx", (unsigned int) from, len); + + if (from >= mtd->size) + return -EINVAL; + + if (len > mtd->size - from) + len = mtd->size - from; + + /* Copy from vram to buf */ + count = len; + while (count) { + unsigned int offset, avail; + unsigned int entry; + + offset = (unsigned int) (from & (priv->cache.page_size - 1)); + avail = priv->cache.page_size - offset; + + mutex_lock(&priv->lock); + + entry = ps3vram_cache_match(mtd, from); + cached = CACHE_OFFSET + entry * priv->cache.page_size + offset; + + dbg("from=%08x cached=%08x offset=%08x avail=%08x count=%08x", + (unsigned)from, cached, offset, avail, count); + + if (avail > count) + avail = count; + memcpy(buf, priv->xdr_buf + cached, avail); + + mutex_unlock(&priv->lock); + + buf += avail; + count -= avail; + from += avail; + } + + *retlen = len; + return 0; +} + +static int ps3vram_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct ps3vram_priv *priv = mtd->priv; + unsigned int cached, count; + + if (to >= mtd->size) + return -EINVAL; + + if (len > mtd->size - to) + len = mtd->size - to; + + /* Copy from buf to vram */ + count = len; + while (count) { + unsigned int offset, avail; + unsigned int entry; + + offset = (unsigned int) (to & (priv->cache.page_size - 1)); + avail = priv->cache.page_size - offset; + + mutex_lock(&priv->lock); + + entry = ps3vram_cache_match(mtd, to); + cached = CACHE_OFFSET + entry * priv->cache.page_size + offset; + + dbg("to=%08x cached=%08x offset=%08x avail=%08x count=%08x", + (unsigned) to, cached, offset, avail, count); + + if (avail > count) + avail = count; + memcpy(priv->xdr_buf + cached, buf, avail); + + priv->cache.tags[entry].flags |= CACHE_PAGE_DIRTY; + + mutex_unlock(&priv->lock); + + buf += avail; + count -= avail; + to += avail; + } + + *retlen = len; + return 0; +} + +static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) +{ + struct ps3vram_priv *priv; + uint64_t status; + uint64_t ddr_lpar, ctrl_lpar, info_lpar, reports_lpar; + int64_t ddr_size; + uint64_t reports_size; + int ret = -ENOMEM; + char *rest; + + ret = -EIO; + ps3vram_mtd.priv = kzalloc(sizeof(struct ps3vram_priv), GFP_KERNEL); + if (!ps3vram_mtd.priv) + goto out; + priv = ps3vram_mtd.priv; + + mutex_init(&priv->lock); + + /* Allocate XDR buffer (1MiB aligned) */ + priv->xdr_buf = (uint8_t *) __get_free_pages(GFP_KERNEL, + get_order(XDR_BUF_SIZE)); + if (priv->xdr_buf == NULL) { + pr_err("ps3vram: could not allocate XDR buffer\n"); + ret = -ENOMEM; + goto out_free_priv; + } + + /* Put FIFO at begginning of XDR buffer */ + priv->fifo_base = (uint32_t *) (priv->xdr_buf + FIFO_OFFSET); + priv->fifo_ptr = priv->fifo_base; + + /* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */ + if (ps3_open_hv_device(dev)) { + pr_err("ps3vram: ps3_open_hv_device failed\n"); + ret = -EAGAIN; + goto out_close_gpu; + } + + /* Request memory */ + status = -1; + ddr_size = memparse(size, &rest); + if (*rest == '-') + ddr_size -= ps3fb_videomemory.size; + ddr_size = ALIGN(ddr_size, 1024*1024); + if (ddr_size <= 0) { + printk(KERN_ERR "ps3vram: specified size is too small\n"); + ret = -EINVAL; + goto out_close_gpu; + } + + while (ddr_size > 0) { + status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0, + &priv->memory_handle, + &ddr_lpar); + if (status == 0) + break; + ddr_size -= 1024*1024; + } + if (status != 0 || ddr_size <= 0) { + pr_err("ps3vram: lv1_gpu_memory_allocate failed\n"); + ret = -ENOMEM; + goto out_free_xdr_buf; + } + pr_info("ps3vram: allocated %u MiB of DDR memory\n", + (unsigned int) (ddr_size / 1024 / 1024)); + + /* Request context */ + status = lv1_gpu_context_allocate(priv->memory_handle, + 0, + &priv->context_handle, + &ctrl_lpar, + &info_lpar, + &reports_lpar, + &reports_size); + if (status) { + pr_err("ps3vram: lv1_gpu_context_allocate failed\n"); + ret = -ENOMEM; + goto out_free_memory; + } + + /* Map XDR buffer to RSX */ + status = lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF, + ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)), + XDR_BUF_SIZE, 0); + if (status) { + pr_err("ps3vram: lv1_gpu_context_iomap failed\n"); + ret = -ENOMEM; + goto out_free_context; + } + + priv->base = ioremap(ddr_lpar, ddr_size); + if (!priv->base) { + pr_err("ps3vram: ioremap failed\n"); + ret = -ENOMEM; + goto out_free_context; + } + + priv->ctrl = ioremap(ctrl_lpar, 64 * 1024); + if (!priv->ctrl) { + pr_err("ps3vram: ioremap failed\n"); + ret = -ENOMEM; + goto out_unmap_vram; + } + + priv->reports = ioremap(reports_lpar, reports_size); + if (!priv->reports) { + pr_err("ps3vram: ioremap failed\n"); + ret = -ENOMEM; + goto out_unmap_ctrl; + } + + mutex_lock(&ps3_gpu_mutex); + ps3vram_init_ring(&ps3vram_mtd); + mutex_unlock(&ps3_gpu_mutex); + + ps3vram_mtd.name = "ps3vram"; + ps3vram_mtd.size = ddr_size; + ps3vram_mtd.flags = MTD_CAP_RAM; + ps3vram_mtd.erase = ps3vram_erase; + ps3vram_mtd.point = NULL; + ps3vram_mtd.unpoint = NULL; + ps3vram_mtd.read = ps3vram_read; + ps3vram_mtd.write = ps3vram_write; + ps3vram_mtd.owner = THIS_MODULE; + ps3vram_mtd.type = MTD_RAM; + ps3vram_mtd.erasesize = CACHE_PAGE_SIZE; + ps3vram_mtd.writesize = 1; + + ps3vram_bind(&ps3vram_mtd); + + mutex_lock(&ps3_gpu_mutex); + ret = ps3vram_wait_ring(&ps3vram_mtd, 100); + mutex_unlock(&ps3_gpu_mutex); + if (ret < 0) { + pr_err("failed to initialize channels\n"); + ret = -ETIMEDOUT; + goto out_unmap_reports; + } + + ps3vram_cache_init(&ps3vram_mtd); + + if (add_mtd_device(&ps3vram_mtd)) { + pr_err("ps3vram: failed to register device\n"); + ret = -EAGAIN; + goto out_cache_cleanup; + } + + pr_info("ps3vram mtd device registered, %lu bytes\n", ddr_size); + return 0; + +out_cache_cleanup: + ps3vram_cache_cleanup(&ps3vram_mtd); +out_unmap_reports: + iounmap(priv->reports); +out_unmap_ctrl: + iounmap(priv->ctrl); +out_unmap_vram: + iounmap(priv->base); +out_free_context: + lv1_gpu_context_free(priv->context_handle); +out_free_memory: + lv1_gpu_memory_free(priv->memory_handle); +out_close_gpu: + ps3_close_hv_device(dev); +out_free_xdr_buf: + free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE)); +out_free_priv: + kfree(ps3vram_mtd.priv); + ps3vram_mtd.priv = NULL; +out: + return ret; +} + +static int ps3vram_shutdown(struct ps3_system_bus_device *dev) +{ + struct ps3vram_priv *priv; + + priv = ps3vram_mtd.priv; + + del_mtd_device(&ps3vram_mtd); + ps3vram_cache_cleanup(&ps3vram_mtd); + iounmap(priv->reports); + iounmap(priv->ctrl); + iounmap(priv->base); + lv1_gpu_context_free(priv->context_handle); + lv1_gpu_memory_free(priv->memory_handle); + ps3_close_hv_device(dev); + free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE)); + kfree(priv); + return 0; +} + +static struct ps3_system_bus_driver ps3vram_driver = { + .match_id = PS3_MATCH_ID_GPU, + .match_sub_id = PS3_MATCH_SUB_ID_GPU_RAMDISK, + .core.name = DEVICE_NAME, + .core.owner = THIS_MODULE, + .probe = ps3vram_probe, + .remove = ps3vram_shutdown, + .shutdown = ps3vram_shutdown, +}; + +static int __init ps3vram_init(void) +{ + return ps3_system_bus_driver_register(&ps3vram_driver); +} + +static void __exit ps3vram_exit(void) +{ + ps3_system_bus_driver_unregister(&ps3vram_driver); +} + +module_init(ps3vram_init); +module_exit(ps3vram_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jim Paris "); +MODULE_DESCRIPTION("MTD driver for PS3 video RAM"); -- cgit v1.2.3 From 0a2d15b928e0b1673d4ed5f48d95af211b6fcc06 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 6 Jan 2009 11:32:03 +0000 Subject: mtd/ps3vram: Add modalias support to the ps3vram driver Update ps3vram driver to use the new ps3 three id modalias support. Signed-off-by: Geert Uytterhoeven Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/ps3.h | 1 + drivers/mtd/devices/ps3vram.c | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/powerpc/include/asm/ps3.h b/arch/powerpc/include/asm/ps3.h index 66b650532adf..eead5c67197a 100644 --- a/arch/powerpc/include/asm/ps3.h +++ b/arch/powerpc/include/asm/ps3.h @@ -333,6 +333,7 @@ enum ps3_match_sub_id { #define PS3_MODULE_ALIAS_STOR_FLASH "ps3:8:0" #define PS3_MODULE_ALIAS_SOUND "ps3:9:0" #define PS3_MODULE_ALIAS_GPU_FB "ps3:10:1" +#define PS3_MODULE_ALIAS_GPU_RAMDISK "ps3:10:2" #define PS3_MODULE_ALIAS_LPM "ps3:11:0" enum ps3_system_bus_device_type { diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c index 26a4b57662f6..22f381fa7844 100644 --- a/drivers/mtd/devices/ps3vram.c +++ b/drivers/mtd/devices/ps3vram.c @@ -774,3 +774,4 @@ module_exit(ps3vram_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jim Paris "); MODULE_DESCRIPTION("MTD driver for PS3 video RAM"); +MODULE_ALIAS(PS3_MODULE_ALIAS_GPU_RAMDISK); -- cgit v1.2.3 From e7dd54cf17714c22665ad74b720f40fb64b3c565 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Tue, 6 Jan 2009 11:32:15 +0000 Subject: mtd/ps3vram: Remove ps3vram debug routines Remove the ps3vram debug routines ps3vram_dump_ring() and ps3vram_dump_reports(). These routines are not needed. Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- drivers/mtd/devices/ps3vram.c | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c index 22f381fa7844..18ca5219a982 100644 --- a/drivers/mtd/devices/ps3vram.c +++ b/drivers/mtd/devices/ps3vram.c @@ -87,9 +87,6 @@ struct ps3vram_priv { #define DMA_NOTIFIER_HANDLE_BASE 0x66604200 /* first DMA notifier handle */ #define DMA_NOTIFIER_OFFSET_BASE 0x1000 /* first DMA notifier offset */ #define DMA_NOTIFIER_SIZE 0x40 - -#define NUM_NOTIFIERS 16 - #define NOTIFIER 7 /* notifier used for completion report */ /* A trailing '-' means to subtract off ps3fb_videomemory.size */ @@ -131,28 +128,6 @@ static int ps3vram_notifier_wait(struct mtd_info *mtd, int timeout_ms) return -1; } -static void ps3vram_dump_ring(struct mtd_info *mtd) -{ - struct ps3vram_priv *priv = mtd->priv; - uint32_t *fifo; - - pr_info("PUT = %08x GET = %08x\n", priv->ctrl[CTRL_PUT], - priv->ctrl[CTRL_GET]); - for (fifo = priv->fifo_base; fifo < priv->fifo_ptr; fifo++) - pr_info("%p: %08x\n", fifo, *fifo); -} - -static void ps3vram_dump_reports(struct mtd_info *mtd) -{ - struct ps3vram_priv *priv = mtd->priv; - int i; - - for (i = 0; i < NUM_NOTIFIERS; i++) { - uint32_t *n = ps3vram_get_notifier(priv->reports, i); - pr_info("%p: %08x\n", n, *n); - } -} - static void ps3vram_init_ring(struct mtd_info *mtd) { struct ps3vram_priv *priv = mtd->priv; @@ -284,8 +259,6 @@ static int ps3vram_upload(struct mtd_info *mtd, unsigned int src_offset, ps3vram_fire_ring(mtd); if (ps3vram_notifier_wait(mtd, 200) < 0) { pr_err("notifier timeout\n"); - ps3vram_dump_ring(mtd); - ps3vram_dump_reports(mtd); return -1; } @@ -317,8 +290,6 @@ static int ps3vram_download(struct mtd_info *mtd, unsigned int src_offset, ps3vram_fire_ring(mtd); if (ps3vram_notifier_wait(mtd, 200) < 0) { pr_err("notifier timeout\n"); - ps3vram_dump_ring(mtd); - ps3vram_dump_reports(mtd); return -1; } -- cgit v1.2.3 From f259d74e030faab15b95fb4bb56d7f424773c278 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Tue, 6 Jan 2009 11:32:21 +0000 Subject: mtd/ps3vram: Cleanup ps3vram driver messages Cleanup the ps3vram driver messages. Add a new struct device pointer variable dev to struct ps3vram_priv and use dev_dbg(), pr_dbg(), etc. where appropriate. Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- drivers/mtd/devices/ps3vram.c | 116 +++++++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c index 18ca5219a982..f5cc2908bdca 100644 --- a/drivers/mtd/devices/ps3vram.c +++ b/drivers/mtd/devices/ps3vram.c @@ -53,9 +53,6 @@ struct mtd_info ps3vram_mtd; #define CACHE_PAGE_PRESENT 1 #define CACHE_PAGE_DIRTY 2 -#define dbg(fmt, args...) \ - pr_debug("%s:%d " fmt "\n", __func__, __LINE__, ## args) - struct ps3vram_tag { unsigned int address; unsigned int flags; @@ -78,6 +75,7 @@ struct ps3vram_priv { uint32_t *fifo_base; uint32_t *fifo_ptr; + struct device *dev; struct ps3vram_cache cache; /* Used to serialize cache/DMA operations */ @@ -148,8 +146,9 @@ static int ps3vram_wait_ring(struct mtd_info *mtd, int timeout) udelay(1); } if (timeout == 0) { - pr_err("FIFO timeout (%08x/%08x/%08x)\n", priv->ctrl[CTRL_PUT], - priv->ctrl[CTRL_GET], priv->ctrl[CTRL_TOP]); + dev_dbg(priv->dev, "%s:%d: FIFO timeout (%08x/%08x/%08x)\n", + __func__, __LINE__, priv->ctrl[CTRL_PUT], + priv->ctrl[CTRL_GET], priv->ctrl[CTRL_TOP]); return -ETIMEDOUT; } @@ -181,7 +180,8 @@ static void ps3vram_rewind_ring(struct mtd_info *mtd) L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, 0, 0, 0, 0); if (status) - pr_err("ps3vram: lv1_gpu_context_attribute FB_BLIT failed\n"); + dev_err(priv->dev, "%s:%d: lv1_gpu_context_attribute failed\n", + __func__, __LINE__); priv->fifo_ptr = priv->fifo_base; } @@ -201,11 +201,13 @@ static void ps3vram_fire_ring(struct mtd_info *mtd) L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, 0, 0, 0, 0); if (status) - pr_err("ps3vram: lv1_gpu_context_attribute FB_BLIT failed\n"); + dev_err(priv->dev, "%s:%d: lv1_gpu_context_attribute failed\n", + __func__, __LINE__); if ((priv->fifo_ptr - priv->fifo_base) * sizeof(uint32_t) > FIFO_SIZE - 1024) { - dbg("fifo full, rewinding"); + dev_dbg(priv->dev, "%s:%d: fifo full, rewinding\n", __func__, + __LINE__); ps3vram_wait_ring(mtd, 200); ps3vram_rewind_ring(mtd); } @@ -258,7 +260,8 @@ static int ps3vram_upload(struct mtd_info *mtd, unsigned int src_offset, ps3vram_out_ring(priv, 0); ps3vram_fire_ring(mtd); if (ps3vram_notifier_wait(mtd, 200) < 0) { - pr_err("notifier timeout\n"); + dev_dbg(priv->dev, "%s:%d: notifier timeout\n", __func__, + __LINE__); return -1; } @@ -289,7 +292,8 @@ static int ps3vram_download(struct mtd_info *mtd, unsigned int src_offset, ps3vram_out_ring(priv, 0); ps3vram_fire_ring(mtd); if (ps3vram_notifier_wait(mtd, 200) < 0) { - pr_err("notifier timeout\n"); + dev_dbg(priv->dev, "%s:%d: notifier timeout\n", __func__, + __LINE__); return -1; } @@ -302,16 +306,17 @@ static void ps3vram_cache_evict(struct mtd_info *mtd, int entry) struct ps3vram_cache *cache = &priv->cache; if (cache->tags[entry].flags & CACHE_PAGE_DIRTY) { - dbg("flushing %d : 0x%08x", entry, cache->tags[entry].address); + dev_dbg(priv->dev, "%s:%d: flushing %d : 0x%08x\n", __func__, + __LINE__, entry, cache->tags[entry].address); if (ps3vram_upload(mtd, CACHE_OFFSET + entry * cache->page_size, cache->tags[entry].address, DMA_PAGE_SIZE, cache->page_size / DMA_PAGE_SIZE) < 0) { - pr_err("failed to upload from 0x%x to 0x%x size 0x%x\n", - entry * cache->page_size, - cache->tags[entry].address, - cache->page_size); + dev_dbg(priv->dev, "%s:%d: failed to upload from " + "0x%x to 0x%x size 0x%x\n", __func__, __LINE__, + entry * cache->page_size, + cache->tags[entry].address, cache->page_size); } cache->tags[entry].flags &= ~CACHE_PAGE_DIRTY; } @@ -323,16 +328,16 @@ static void ps3vram_cache_load(struct mtd_info *mtd, int entry, struct ps3vram_priv *priv = mtd->priv; struct ps3vram_cache *cache = &priv->cache; - dbg("fetching %d : 0x%08x", entry, address); + dev_dbg(priv->dev, "%s:%d: fetching %d : 0x%08x\n", __func__, __LINE__, + entry, address); if (ps3vram_download(mtd, address, CACHE_OFFSET + entry * cache->page_size, DMA_PAGE_SIZE, cache->page_size / DMA_PAGE_SIZE) < 0) { - pr_err("failed to download from 0x%x to 0x%x size 0x%x\n", - address, - entry * cache->page_size, - cache->page_size); + dev_err(priv->dev, "%s:%d: failed to download from " + "0x%x to 0x%x size 0x%x\n", __func__, __LINE__, address, + entry * cache->page_size, cache->page_size); } cache->tags[entry].address = address; @@ -346,7 +351,7 @@ static void ps3vram_cache_flush(struct mtd_info *mtd) struct ps3vram_cache *cache = &priv->cache; int i; - dbg("FLUSH"); + dev_dbg(priv->dev, "%s:%d: FLUSH\n", __func__, __LINE__); for (i = 0; i < cache->page_count; i++) { ps3vram_cache_evict(mtd, i); cache->tags[i].flags = 0; @@ -369,15 +374,15 @@ static unsigned int ps3vram_cache_match(struct mtd_info *mtd, loff_t address) for (i = 0; i < cache->page_count; i++) { if ((cache->tags[i].flags & CACHE_PAGE_PRESENT) && cache->tags[i].address == base) { - dbg("found entry %d : 0x%08x", - i, cache->tags[i].address); + dev_dbg(priv->dev, "%s:%d: found entry %d : 0x%08x\n", + __func__, __LINE__, i, cache->tags[i].address); return i; } } /* choose a random entry */ i = (jiffies + (counter++)) % cache->page_count; - dbg("using cache entry %d", i); + dev_dbg(priv->dev, "%s:%d: using entry %d\n", __func__, __LINE__, i); ps3vram_cache_evict(mtd, i); ps3vram_cache_load(mtd, i, base); @@ -389,18 +394,19 @@ static int ps3vram_cache_init(struct mtd_info *mtd) { struct ps3vram_priv *priv = mtd->priv; - pr_info("creating cache: %d entries, %d bytes pages\n", - CACHE_PAGE_COUNT, CACHE_PAGE_SIZE); - priv->cache.page_count = CACHE_PAGE_COUNT; priv->cache.page_size = CACHE_PAGE_SIZE; priv->cache.tags = kzalloc(sizeof(struct ps3vram_tag) * CACHE_PAGE_COUNT, GFP_KERNEL); if (priv->cache.tags == NULL) { - pr_err("could not allocate cache tags\n"); + dev_err(priv->dev, "%s:%d: could not allocate cache tags\n", + __func__, __LINE__); return -ENOMEM; } + dev_info(priv->dev, "created ram cache: %d entries, %d KiB each\n", + CACHE_PAGE_COUNT, CACHE_PAGE_SIZE / 1024); + return 0; } @@ -434,14 +440,14 @@ static int ps3vram_erase(struct mtd_info *mtd, struct erase_info *instr) return 0; } - static int ps3vram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct ps3vram_priv *priv = mtd->priv; unsigned int cached, count; - dbg("from = 0x%08x len = 0x%zx", (unsigned int) from, len); + dev_dbg(priv->dev, "%s:%d: from=0x%08x len=0x%zx\n", __func__, __LINE__, + (unsigned int)from, len); if (from >= mtd->size) return -EINVAL; @@ -463,8 +469,9 @@ static int ps3vram_read(struct mtd_info *mtd, loff_t from, size_t len, entry = ps3vram_cache_match(mtd, from); cached = CACHE_OFFSET + entry * priv->cache.page_size + offset; - dbg("from=%08x cached=%08x offset=%08x avail=%08x count=%08x", - (unsigned)from, cached, offset, avail, count); + dev_dbg(priv->dev, "%s:%d: from=%08x cached=%08x offset=%08x " + "avail=%08x count=%08x\n", __func__, __LINE__, + (unsigned int)from, cached, offset, avail, count); if (avail > count) avail = count; @@ -507,8 +514,9 @@ static int ps3vram_write(struct mtd_info *mtd, loff_t to, size_t len, entry = ps3vram_cache_match(mtd, to); cached = CACHE_OFFSET + entry * priv->cache.page_size + offset; - dbg("to=%08x cached=%08x offset=%08x avail=%08x count=%08x", - (unsigned) to, cached, offset, avail, count); + dev_dbg(priv->dev, "%s:%d: to=%08x cached=%08x offset=%08x " + "avail=%08x count=%08x\n", __func__, __LINE__, + (unsigned int)to, cached, offset, avail, count); if (avail > count) avail = count; @@ -544,12 +552,14 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) priv = ps3vram_mtd.priv; mutex_init(&priv->lock); + priv->dev = &dev->core; /* Allocate XDR buffer (1MiB aligned) */ priv->xdr_buf = (uint8_t *) __get_free_pages(GFP_KERNEL, get_order(XDR_BUF_SIZE)); if (priv->xdr_buf == NULL) { - pr_err("ps3vram: could not allocate XDR buffer\n"); + dev_dbg(&dev->core, "%s:%d: could not allocate XDR buffer\n", + __func__, __LINE__); ret = -ENOMEM; goto out_free_priv; } @@ -560,7 +570,8 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) /* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */ if (ps3_open_hv_device(dev)) { - pr_err("ps3vram: ps3_open_hv_device failed\n"); + dev_err(&dev->core, "%s:%d: ps3_open_hv_device failed\n", + __func__, __LINE__); ret = -EAGAIN; goto out_close_gpu; } @@ -572,7 +583,8 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) ddr_size -= ps3fb_videomemory.size; ddr_size = ALIGN(ddr_size, 1024*1024); if (ddr_size <= 0) { - printk(KERN_ERR "ps3vram: specified size is too small\n"); + dev_err(&dev->core, "%s:%d: specified size is too small\n", + __func__, __LINE__); ret = -EINVAL; goto out_close_gpu; } @@ -586,12 +598,11 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) ddr_size -= 1024*1024; } if (status != 0 || ddr_size <= 0) { - pr_err("ps3vram: lv1_gpu_memory_allocate failed\n"); + dev_err(&dev->core, "%s:%d: lv1_gpu_memory_allocate failed\n", + __func__, __LINE__); ret = -ENOMEM; goto out_free_xdr_buf; } - pr_info("ps3vram: allocated %u MiB of DDR memory\n", - (unsigned int) (ddr_size / 1024 / 1024)); /* Request context */ status = lv1_gpu_context_allocate(priv->memory_handle, @@ -602,7 +613,8 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) &reports_lpar, &reports_size); if (status) { - pr_err("ps3vram: lv1_gpu_context_allocate failed\n"); + dev_err(&dev->core, "%s:%d: lv1_gpu_context_allocate failed\n", + __func__, __LINE__); ret = -ENOMEM; goto out_free_memory; } @@ -612,28 +624,32 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)), XDR_BUF_SIZE, 0); if (status) { - pr_err("ps3vram: lv1_gpu_context_iomap failed\n"); + dev_err(&dev->core, "%s:%d: lv1_gpu_context_iomap failed\n", + __func__, __LINE__); ret = -ENOMEM; goto out_free_context; } priv->base = ioremap(ddr_lpar, ddr_size); if (!priv->base) { - pr_err("ps3vram: ioremap failed\n"); + dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__, + __LINE__); ret = -ENOMEM; goto out_free_context; } priv->ctrl = ioremap(ctrl_lpar, 64 * 1024); if (!priv->ctrl) { - pr_err("ps3vram: ioremap failed\n"); + dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__, + __LINE__); ret = -ENOMEM; goto out_unmap_vram; } priv->reports = ioremap(reports_lpar, reports_size); if (!priv->reports) { - pr_err("ps3vram: ioremap failed\n"); + dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__, + __LINE__); ret = -ENOMEM; goto out_unmap_ctrl; } @@ -661,7 +677,8 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) ret = ps3vram_wait_ring(&ps3vram_mtd, 100); mutex_unlock(&ps3_gpu_mutex); if (ret < 0) { - pr_err("failed to initialize channels\n"); + dev_err(&dev->core, "%s:%d: failed to initialize channels\n", + __func__, __LINE__); ret = -ETIMEDOUT; goto out_unmap_reports; } @@ -669,12 +686,15 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) ps3vram_cache_init(&ps3vram_mtd); if (add_mtd_device(&ps3vram_mtd)) { - pr_err("ps3vram: failed to register device\n"); + dev_err(&dev->core, "%s:%d: add_mtd_device failed\n", + __func__, __LINE__); ret = -EAGAIN; goto out_cache_cleanup; } - pr_info("ps3vram mtd device registered, %lu bytes\n", ddr_size); + dev_info(&dev->core, "reserved %u MiB of gpu memory\n", + (unsigned int)(ddr_size / 1024 / 1024)); + return 0; out_cache_cleanup: -- cgit v1.2.3 From 993e62e674ba670341f11f60398446bb37a88e8b Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Tue, 6 Jan 2009 11:32:28 +0000 Subject: mtd/ps3vram: Use proper kernel types Replace the use of stdint.h types with kernel types in the ps3vram driver. Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- drivers/mtd/devices/ps3vram.c | 56 +++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c index f5cc2908bdca..91cc2afbe14e 100644 --- a/drivers/mtd/devices/ps3vram.c +++ b/drivers/mtd/devices/ps3vram.c @@ -65,15 +65,15 @@ struct ps3vram_cache { }; struct ps3vram_priv { - uint64_t memory_handle; - uint64_t context_handle; - uint8_t *base; - uint32_t *ctrl; - uint32_t *reports; - uint8_t *xdr_buf; + u64 memory_handle; + u64 context_handle; + u32 *ctrl; + u32 *reports; + u8 *base; + u8 *xdr_buf; - uint32_t *fifo_base; - uint32_t *fifo_ptr; + u32 *fifo_base; + u32 *fifo_ptr; struct device *dev; struct ps3vram_cache cache; @@ -92,7 +92,7 @@ char *size = "256M-"; module_param(size, charp, 0); MODULE_PARM_DESC(size, "memory size"); -static inline uint32_t *ps3vram_get_notifier(uint32_t *reports, int notifier) +static u32 *ps3vram_get_notifier(u32 *reports, int notifier) { return (void *) reports + DMA_NOTIFIER_OFFSET_BASE + @@ -102,8 +102,9 @@ static inline uint32_t *ps3vram_get_notifier(uint32_t *reports, int notifier) static void ps3vram_notifier_reset(struct mtd_info *mtd) { int i; + struct ps3vram_priv *priv = mtd->priv; - uint32_t *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); + u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); for (i = 0; i < 4; i++) notify[i] = 0xffffffff; } @@ -111,7 +112,7 @@ static void ps3vram_notifier_reset(struct mtd_info *mtd) static int ps3vram_notifier_wait(struct mtd_info *mtd, int timeout_ms) { struct ps3vram_priv *priv = mtd->priv; - uint32_t *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); + u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); timeout_ms *= 1000; @@ -155,13 +156,13 @@ static int ps3vram_wait_ring(struct mtd_info *mtd, int timeout) return 0; } -static inline void ps3vram_out_ring(struct ps3vram_priv *priv, uint32_t data) +static void ps3vram_out_ring(struct ps3vram_priv *priv, u32 data) { *(priv->fifo_ptr)++ = data; } -static inline void ps3vram_begin_ring(struct ps3vram_priv *priv, uint32_t chan, - uint32_t tag, uint32_t size) +static void ps3vram_begin_ring(struct ps3vram_priv *priv, u32 chan, + u32 tag, u32 size) { ps3vram_out_ring(priv, (size << 18) | (chan << 13) | tag); } @@ -194,7 +195,7 @@ static void ps3vram_fire_ring(struct mtd_info *mtd) mutex_lock(&ps3_gpu_mutex); priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET + - (priv->fifo_ptr - priv->fifo_base) * sizeof(uint32_t); + (priv->fifo_ptr - priv->fifo_base) * sizeof(u32); /* asking the HV for a blit will kick the fifo */ status = lv1_gpu_context_attribute(priv->context_handle, @@ -204,8 +205,8 @@ static void ps3vram_fire_ring(struct mtd_info *mtd) dev_err(priv->dev, "%s:%d: lv1_gpu_context_attribute failed\n", __func__, __LINE__); - if ((priv->fifo_ptr - priv->fifo_base) * sizeof(uint32_t) > - FIFO_SIZE - 1024) { + if ((priv->fifo_ptr - priv->fifo_base) * sizeof(u32) > + FIFO_SIZE - 1024) { dev_dbg(priv->dev, "%s:%d: fifo full, rewinding\n", __func__, __LINE__); ps3vram_wait_ring(mtd, 200); @@ -538,10 +539,13 @@ static int ps3vram_write(struct mtd_info *mtd, loff_t to, size_t len, static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) { struct ps3vram_priv *priv; - uint64_t status; - uint64_t ddr_lpar, ctrl_lpar, info_lpar, reports_lpar; - int64_t ddr_size; - uint64_t reports_size; + int status; + u64 ddr_lpar; + u64 ctrl_lpar; + u64 info_lpar; + u64 reports_lpar; + u64 ddr_size; + u64 reports_size; int ret = -ENOMEM; char *rest; @@ -555,8 +559,8 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) priv->dev = &dev->core; /* Allocate XDR buffer (1MiB aligned) */ - priv->xdr_buf = (uint8_t *) __get_free_pages(GFP_KERNEL, - get_order(XDR_BUF_SIZE)); + priv->xdr_buf = (void *)__get_free_pages(GFP_KERNEL, + get_order(XDR_BUF_SIZE)); if (priv->xdr_buf == NULL) { dev_dbg(&dev->core, "%s:%d: could not allocate XDR buffer\n", __func__, __LINE__); @@ -565,7 +569,7 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) } /* Put FIFO at begginning of XDR buffer */ - priv->fifo_base = (uint32_t *) (priv->xdr_buf + FIFO_OFFSET); + priv->fifo_base = (u32 *) (priv->xdr_buf + FIFO_OFFSET); priv->fifo_ptr = priv->fifo_base; /* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */ @@ -593,11 +597,11 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0, &priv->memory_handle, &ddr_lpar); - if (status == 0) + if (!status) break; ddr_size -= 1024*1024; } - if (status != 0 || ddr_size <= 0) { + if (status || ddr_size <= 0) { dev_err(&dev->core, "%s:%d: lv1_gpu_memory_allocate failed\n", __func__, __LINE__); ret = -ENOMEM; -- cgit v1.2.3 From 60c0c5987b0996a7c7c4c6d90f63ed413c368a71 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Wed, 7 Jan 2009 17:22:02 -0800 Subject: mtd/ps3vram: Use msleep in waits Replace the use of udelay() with msleep() in the looping wait routines ps3vram_notifier_wait() and ps3vram_wait_ring(). Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- drivers/mtd/devices/ps3vram.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c index 91cc2afbe14e..d5924142d909 100644 --- a/drivers/mtd/devices/ps3vram.c +++ b/drivers/mtd/devices/ps3vram.c @@ -109,22 +109,19 @@ static void ps3vram_notifier_reset(struct mtd_info *mtd) notify[i] = 0xffffffff; } -static int ps3vram_notifier_wait(struct mtd_info *mtd, int timeout_ms) +static int ps3vram_notifier_wait(struct mtd_info *mtd, unsigned int timeout_ms) { struct ps3vram_priv *priv = mtd->priv; u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); - - timeout_ms *= 1000; + unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); do { - if (notify[3] == 0) + if (!notify[3]) return 0; + msleep(1); + } while (time_before(jiffies, timeout)); - if (timeout_ms) - udelay(1); - } while (timeout_ms--); - - return -1; + return -ETIMEDOUT; } static void ps3vram_init_ring(struct mtd_info *mtd) @@ -135,25 +132,22 @@ static void ps3vram_init_ring(struct mtd_info *mtd) priv->ctrl[CTRL_GET] = FIFO_BASE + FIFO_OFFSET; } -static int ps3vram_wait_ring(struct mtd_info *mtd, int timeout) +static int ps3vram_wait_ring(struct mtd_info *mtd, unsigned int timeout_ms) { struct ps3vram_priv *priv = mtd->priv; + unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); - /* wait until setup commands are processed */ - timeout *= 1000; - while (--timeout) { + do { if (priv->ctrl[CTRL_PUT] == priv->ctrl[CTRL_GET]) - break; - udelay(1); - } - if (timeout == 0) { - dev_dbg(priv->dev, "%s:%d: FIFO timeout (%08x/%08x/%08x)\n", - __func__, __LINE__, priv->ctrl[CTRL_PUT], - priv->ctrl[CTRL_GET], priv->ctrl[CTRL_TOP]); - return -ETIMEDOUT; - } + return 0; + msleep(1); + } while (time_before(jiffies, timeout)); - return 0; + dev_dbg(priv->dev, "%s:%d: FIFO timeout (%08x/%08x/%08x)\n", __func__, + __LINE__, priv->ctrl[CTRL_PUT], priv->ctrl[CTRL_GET], + priv->ctrl[CTRL_TOP]); + + return -ETIMEDOUT; } static void ps3vram_out_ring(struct ps3vram_priv *priv, u32 data) -- cgit v1.2.3 From 2efd72af0f18860927084df618f7419c82f69be3 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Wed, 7 Jan 2009 17:22:07 -0800 Subject: mtd/ps3vram: Use _PAGE_NO_CACHE in memory ioremap Use _PAGE_NO_CACHE for gpu memory ioremap. Also, add __iomem attribute to gpu memory pointer and change use of memset() to memset_io(). Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- drivers/mtd/devices/ps3vram.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c index d5924142d909..d21e9beb7ed2 100644 --- a/drivers/mtd/devices/ps3vram.c +++ b/drivers/mtd/devices/ps3vram.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -69,7 +70,7 @@ struct ps3vram_priv { u64 context_handle; u32 *ctrl; u32 *reports; - u8 *base; + u8 __iomem *ddr_base; u8 *xdr_buf; u32 *fifo_base; @@ -425,7 +426,7 @@ static int ps3vram_erase(struct mtd_info *mtd, struct erase_info *instr) ps3vram_cache_flush(mtd); /* Set bytes to 0xFF */ - memset(priv->base + instr->addr, 0xFF, instr->len); + memset_io(priv->ddr_base + instr->addr, 0xFF, instr->len); mutex_unlock(&priv->lock); @@ -628,8 +629,9 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) goto out_free_context; } - priv->base = ioremap(ddr_lpar, ddr_size); - if (!priv->base) { + priv->ddr_base = ioremap_flags(ddr_lpar, ddr_size, _PAGE_NO_CACHE); + + if (!priv->ddr_base) { dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__, __LINE__); ret = -ENOMEM; @@ -702,7 +704,7 @@ out_unmap_reports: out_unmap_ctrl: iounmap(priv->ctrl); out_unmap_vram: - iounmap(priv->base); + iounmap(priv->ddr_base); out_free_context: lv1_gpu_context_free(priv->context_handle); out_free_memory: @@ -728,7 +730,7 @@ static int ps3vram_shutdown(struct ps3_system_bus_device *dev) ps3vram_cache_cleanup(&ps3vram_mtd); iounmap(priv->reports); iounmap(priv->ctrl); - iounmap(priv->base); + iounmap(priv->ddr_base); lv1_gpu_context_free(priv->context_handle); lv1_gpu_memory_free(priv->memory_handle); ps3_close_hv_device(dev); -- cgit v1.2.3 From 63277161312dd42af7dd3968077b272d192dd6ba Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 6 Jan 2009 13:54:25 +0000 Subject: powerpc: Remove unnecessary casts of_get_flat_dt_prop() returns a "void *", so we don't need to cast when assigning its result to a pointer variable. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/prom.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 6f73c739f1e2..c09cffafb6ee 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -824,11 +824,11 @@ static int __init early_init_dt_scan_chosen(unsigned long node, #endif #ifdef CONFIG_KEXEC - lprop = (u64*)of_get_flat_dt_prop(node, "linux,crashkernel-base", NULL); + lprop = of_get_flat_dt_prop(node, "linux,crashkernel-base", NULL); if (lprop) crashk_res.start = *lprop; - lprop = (u64*)of_get_flat_dt_prop(node, "linux,crashkernel-size", NULL); + lprop = of_get_flat_dt_prop(node, "linux,crashkernel-size", NULL); if (lprop) crashk_res.end = crashk_res.start + *lprop - 1; #endif @@ -893,12 +893,12 @@ static int __init early_init_dt_scan_drconf_memory(unsigned long node) u64 base, size, lmb_size; unsigned int is_kexec_kdump = 0, rngs; - ls = (cell_t *)of_get_flat_dt_prop(node, "ibm,lmb-size", &l); + ls = of_get_flat_dt_prop(node, "ibm,lmb-size", &l); if (ls == NULL || l < dt_root_size_cells * sizeof(cell_t)) return 0; lmb_size = dt_mem_next_cell(dt_root_size_cells, &ls); - dm = (cell_t *)of_get_flat_dt_prop(node, "ibm,dynamic-memory", &l); + dm = of_get_flat_dt_prop(node, "ibm,dynamic-memory", &l); if (dm == NULL || l < sizeof(cell_t)) return 0; @@ -907,7 +907,7 @@ static int __init early_init_dt_scan_drconf_memory(unsigned long node) return 0; /* check if this is a kexec/kdump kernel. */ - usm = (cell_t *)of_get_flat_dt_prop(node, "linux,drconf-usable-memory", + usm = of_get_flat_dt_prop(node, "linux,drconf-usable-memory", &l); if (usm != NULL) is_kexec_kdump = 1; @@ -981,9 +981,9 @@ static int __init early_init_dt_scan_memory(unsigned long node, } else if (strcmp(type, "memory") != 0) return 0; - reg = (cell_t *)of_get_flat_dt_prop(node, "linux,usable-memory", &l); + reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l); if (reg == NULL) - reg = (cell_t *)of_get_flat_dt_prop(node, "reg", &l); + reg = of_get_flat_dt_prop(node, "reg", &l); if (reg == NULL) return 0; -- cgit v1.2.3 From 2b931fb67e10d6eb99d9928fa3afe83cdeeb7354 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 6 Jan 2009 13:56:52 +0000 Subject: powerpc: Use correct type in prom_init.c tce_entryp is a "u64 *" not an "unsigned long *". [Split from a large patch -sfr] Signed-off-by: Ingo Molnar Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/prom_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 2445945d3761..7f1b33d5e30d 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -1210,7 +1210,7 @@ static void __init prom_initialize_tce_table(void) /* Initialize the table to have a one-to-one mapping * over the allocated size. */ - tce_entryp = (unsigned long *)base; + tce_entryp = (u64 *)base; for (i = 0; i < (minsize >> 3) ;tce_entryp++, i++) { tce_entry = (i << PAGE_SHIFT); tce_entry |= 0x3; -- cgit v1.2.3 From 19b0bd025d6647549e07becf02b99e5168c17432 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 6 Jan 2009 14:01:23 +0000 Subject: powerpc/cell: Use correct types in beat files Only pass the address of a u64 if that is what the function requires. [Split out of a larger patch - sfr] [update comment - sfr] Signed-off-by: Ingo Molnar Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/cell/beat_htab.c | 21 +++++++++++---------- arch/powerpc/platforms/cell/beat_udbg.c | 4 ++-- drivers/char/hvc_beat.c | 4 ++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/arch/powerpc/platforms/cell/beat_htab.c b/arch/powerpc/platforms/cell/beat_htab.c index 2e67bd840e01..35b1ec492715 100644 --- a/arch/powerpc/platforms/cell/beat_htab.c +++ b/arch/powerpc/platforms/cell/beat_htab.c @@ -44,8 +44,8 @@ static DEFINE_SPINLOCK(beat_htab_lock); static inline unsigned int beat_read_mask(unsigned hpte_group) { - unsigned long hpte_v[5]; unsigned long rmask = 0; + u64 hpte_v[5]; beat_read_htab_entries(0, hpte_group + 0, hpte_v); if (!(hpte_v[0] & HPTE_V_BOLTED)) @@ -93,8 +93,7 @@ static long beat_lpar_hpte_insert(unsigned long hpte_group, int psize, int ssize) { unsigned long lpar_rc; - unsigned long slot; - unsigned long hpte_v, hpte_r; + u64 hpte_v, hpte_r, slot; /* same as iseries */ if (vflags & HPTE_V_SECONDARY) @@ -153,8 +152,9 @@ static long beat_lpar_hpte_remove(unsigned long hpte_group) static unsigned long beat_lpar_hpte_getword0(unsigned long slot) { - unsigned long dword0, dword[5]; + unsigned long dword0; unsigned long lpar_rc; + u64 dword[5]; lpar_rc = beat_read_htab_entries(0, slot & ~3UL, dword); @@ -170,7 +170,7 @@ static void beat_lpar_hptab_clear(void) unsigned long size_bytes = 1UL << ppc64_pft_size; unsigned long hpte_count = size_bytes >> 4; int i; - unsigned long dummy0, dummy1; + u64 dummy0, dummy1; /* TODO: Use bulk call */ for (i = 0; i < hpte_count; i++) @@ -189,7 +189,8 @@ static long beat_lpar_hpte_updatepp(unsigned long slot, int psize, int ssize, int local) { unsigned long lpar_rc; - unsigned long dummy0, dummy1, want_v; + u64 dummy0, dummy1; + unsigned long want_v; want_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M); @@ -255,7 +256,8 @@ static void beat_lpar_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, int psize, int ssize) { - unsigned long lpar_rc, slot, vsid, va, dummy0, dummy1; + unsigned long lpar_rc, slot, vsid, va; + u64 dummy0, dummy1; vsid = get_kernel_vsid(ea, MMU_SEGSIZE_256M); va = (vsid << 28) | (ea & 0x0fffffff); @@ -276,7 +278,7 @@ static void beat_lpar_hpte_invalidate(unsigned long slot, unsigned long va, { unsigned long want_v; unsigned long lpar_rc; - unsigned long dummy1, dummy2; + u64 dummy1, dummy2; unsigned long flags; DBG_LOW(" inval : slot=%lx, va=%016lx, psize: %d, local: %d\n", @@ -315,8 +317,7 @@ static long beat_lpar_hpte_insert_v3(unsigned long hpte_group, int psize, int ssize) { unsigned long lpar_rc; - unsigned long slot; - unsigned long hpte_v, hpte_r; + u64 hpte_v, hpte_r, slot; /* same as iseries */ if (vflags & HPTE_V_SECONDARY) diff --git a/arch/powerpc/platforms/cell/beat_udbg.c b/arch/powerpc/platforms/cell/beat_udbg.c index 6b418f6b6175..350735bc8888 100644 --- a/arch/powerpc/platforms/cell/beat_udbg.c +++ b/arch/powerpc/platforms/cell/beat_udbg.c @@ -40,8 +40,8 @@ static void udbg_putc_beat(char c) } /* Buffered chars getc */ -static long inbuflen; -static long inbuf[2]; /* must be 2 longs */ +static u64 inbuflen; +static u64 inbuf[2]; /* must be 2 u64s */ static int udbg_getc_poll_beat(void) { diff --git a/drivers/char/hvc_beat.c b/drivers/char/hvc_beat.c index 91cdb35a9204..0afc8b82212e 100644 --- a/drivers/char/hvc_beat.c +++ b/drivers/char/hvc_beat.c @@ -44,7 +44,7 @@ static int hvc_beat_get_chars(uint32_t vtermno, char *buf, int cnt) static unsigned char q[sizeof(unsigned long) * 2] __attribute__((aligned(sizeof(unsigned long)))); static int qlen = 0; - unsigned long got; + u64 got; again: if (qlen) { @@ -63,7 +63,7 @@ again: } } if (beat_get_term_char(vtermno, &got, - ((unsigned long *)q), ((unsigned long *)q) + 1) == 0) { + ((u64 *)q), ((u64 *)q) + 1) == 0) { qlen = got; goto again; } -- cgit v1.2.3 From b36ac9e84b4a3602bd07c2b7cf995f88f76d8428 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 6 Jan 2009 14:03:44 +0000 Subject: powerpc/cell: Fix some u64 vs. long types in/out_be64() work on u64s. The first parameter to ppc_md.ioremap is a phys_addr_t. Signed-off-by: Ingo Molnar Signed-off-by: Stephen Rothwell Acked-by: Arnd Bergmann Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/cell/interrupt.c | 2 +- arch/powerpc/platforms/cell/io-workarounds.c | 4 ++-- arch/powerpc/platforms/cell/iommu.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c index 2d5bb22d6c09..28c04dab2633 100644 --- a/arch/powerpc/platforms/cell/interrupt.c +++ b/arch/powerpc/platforms/cell/interrupt.c @@ -148,7 +148,7 @@ static unsigned int iic_get_irq(void) iic = &__get_cpu_var(iic); *(unsigned long *) &pending = - in_be64((unsigned long __iomem *) &iic->regs->pending_destr); + in_be64((u64 __iomem *) &iic->regs->pending_destr); if (!(pending.flags & CBE_IIC_IRQ_VALID)) return NO_IRQ; virq = irq_linear_revmap(iic_host, iic_pending_to_hwnum(pending)); diff --git a/arch/powerpc/platforms/cell/io-workarounds.c b/arch/powerpc/platforms/cell/io-workarounds.c index b5f84e8f0899..059cad6c3f69 100644 --- a/arch/powerpc/platforms/cell/io-workarounds.c +++ b/arch/powerpc/platforms/cell/io-workarounds.c @@ -130,14 +130,14 @@ static const struct ppc_pci_io __devinitconst iowa_pci_io = { }; -static void __iomem *iowa_ioremap(unsigned long addr, unsigned long size, +static void __iomem *iowa_ioremap(phys_addr_t addr, unsigned long size, unsigned long flags) { struct iowa_bus *bus; void __iomem *res = __ioremap(addr, size, flags); int busno; - bus = iowa_pci_find(0, addr); + bus = iowa_pci_find(0, (unsigned long)addr); if (bus != NULL) { busno = bus - iowa_busses; PCI_SET_ADDR_TOKEN(res, busno + 1); diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index 86db4dd170a0..88d94b59a7cb 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -150,8 +150,8 @@ static int cbe_nr_iommus; static void invalidate_tce_cache(struct cbe_iommu *iommu, unsigned long *pte, long n_ptes) { - unsigned long __iomem *reg; - unsigned long val; + u64 __iomem *reg; + u64 val; long n; reg = iommu->xlate_regs + IOC_IOPT_CacheInvd; -- cgit v1.2.3 From ac3f6454d1ee0eb68485e05bb068de7c22e730d7 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 6 Jan 2009 14:06:28 +0000 Subject: powerpc/pasemi: local_irq_save uses an unsigned long [Split from a larger patch - sfr] Signed-off-by: Ingo Molnar Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pasemi/cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pasemi/cpufreq.c b/arch/powerpc/platforms/pasemi/cpufreq.c index 58556b028a4c..86db47c1b665 100644 --- a/arch/powerpc/platforms/pasemi/cpufreq.c +++ b/arch/powerpc/platforms/pasemi/cpufreq.c @@ -112,7 +112,7 @@ static int get_gizmo_latency(void) static void set_astate(int cpu, unsigned int astate) { - u64 flags; + unsigned long flags; /* Return if called before init has run */ if (unlikely(!sdcasr_mapbase)) -- cgit v1.2.3 From 4a0826824beb28390651a962987b0681b9e7fe93 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 6 Jan 2009 17:56:51 +0000 Subject: powerpc: Fix missing semicolons in mmu_decl.h This is a brown paper bag from one of my earlier patches that breaks build on 40x and 8xx. And yes, I've now added 40x and 8xx to my list of test configs :-) Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/mmu_decl.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/mm/mmu_decl.h b/arch/powerpc/mm/mmu_decl.h index 4314b39b6faf..ad123bced404 100644 --- a/arch/powerpc/mm/mmu_decl.h +++ b/arch/powerpc/mm/mmu_decl.h @@ -30,11 +30,11 @@ #if defined(CONFIG_40x) || defined(CONFIG_8xx) static inline void _tlbil_all(void) { - asm volatile ("sync; tlbia; isync" : : : "memory") + asm volatile ("sync; tlbia; isync" : : : "memory"); } static inline void _tlbil_pid(unsigned int pid) { - asm volatile ("sync; tlbia; isync" : : : "memory") + asm volatile ("sync; tlbia; isync" : : : "memory"); } #else /* CONFIG_40x || CONFIG_8xx */ extern void _tlbil_all(void); @@ -47,7 +47,7 @@ extern void _tlbil_pid(unsigned int pid); #ifdef CONFIG_8xx static inline void _tlbil_va(unsigned long address, unsigned int pid) { - asm volatile ("tlbie %0; sync" : : "r" (address) : "memory") + asm volatile ("tlbie %0; sync" : : "r" (address) : "memory"); } #else /* CONFIG_8xx */ extern void _tlbil_va(unsigned long address, unsigned int pid); -- cgit v1.2.3 From 1edda9c795b99c3761715a73f62a78fce41a1f1d Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Tue, 6 Jan 2009 23:00:05 -0600 Subject: powerpc: Export cacheable_memzero as its now used in a driver The Freescale PowerPC specific gianfar driver (gig-e) uses cacheable_memzero for performance reasons we need to export the symbol to allow the driver to be built as a module. Signed-off-by: Kumar Gala Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/ppc_ksyms.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c index dcec1325d340..c8b27bb4dbde 100644 --- a/arch/powerpc/kernel/ppc_ksyms.c +++ b/arch/powerpc/kernel/ppc_ksyms.c @@ -165,6 +165,7 @@ EXPORT_SYMBOL(timer_interrupt); EXPORT_SYMBOL(irq_desc); EXPORT_SYMBOL(tb_ticks_per_jiffy); EXPORT_SYMBOL(cacheable_memcpy); +EXPORT_SYMBOL(cacheable_memzero); #endif #ifdef CONFIG_PPC32 -- cgit v1.2.3 From 02af87a74271977d09ece9b709909dcae3f9fab9 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Wed, 17 Dec 2008 10:09:35 +0000 Subject: powerpc/kdump: Use ppc_save_regs() in crash_setup_regs() The patch replaces internal registers dump implementation with ppc_save_regs(). From now on PPC64 and PPC32 are using the same code for crash_setup_regs(). NOTE: The old regs dump implementation was capturing SP (r1) directly as is, so you could see crash_kexec() function on top of the back-trace. But ppc_save_regs() goes up one stack frame, so you'll not see it anymore, at the top-level you'll see who actually triggered the crash dump instead. Signed-off-by: Anton Vorontsov Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/kexec.h | 55 ---------------------------------------- 1 file changed, 55 deletions(-) diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h index 6dbffc981702..7e06b43720d3 100644 --- a/arch/powerpc/include/asm/kexec.h +++ b/arch/powerpc/include/asm/kexec.h @@ -48,63 +48,8 @@ static inline void crash_setup_regs(struct pt_regs *newregs, { if (oldregs) memcpy(newregs, oldregs, sizeof(*newregs)); -#ifdef __powerpc64__ - else { - /* FIXME Merge this with xmon_save_regs ?? */ - unsigned long tmp1, tmp2; - __asm__ __volatile__ ( - "std 0,0(%2)\n" - "std 1,8(%2)\n" - "std 2,16(%2)\n" - "std 3,24(%2)\n" - "std 4,32(%2)\n" - "std 5,40(%2)\n" - "std 6,48(%2)\n" - "std 7,56(%2)\n" - "std 8,64(%2)\n" - "std 9,72(%2)\n" - "std 10,80(%2)\n" - "std 11,88(%2)\n" - "std 12,96(%2)\n" - "std 13,104(%2)\n" - "std 14,112(%2)\n" - "std 15,120(%2)\n" - "std 16,128(%2)\n" - "std 17,136(%2)\n" - "std 18,144(%2)\n" - "std 19,152(%2)\n" - "std 20,160(%2)\n" - "std 21,168(%2)\n" - "std 22,176(%2)\n" - "std 23,184(%2)\n" - "std 24,192(%2)\n" - "std 25,200(%2)\n" - "std 26,208(%2)\n" - "std 27,216(%2)\n" - "std 28,224(%2)\n" - "std 29,232(%2)\n" - "std 30,240(%2)\n" - "std 31,248(%2)\n" - "mfmsr %0\n" - "std %0, 264(%2)\n" - "mfctr %0\n" - "std %0, 280(%2)\n" - "mflr %0\n" - "std %0, 288(%2)\n" - "bl 1f\n" - "1: mflr %1\n" - "std %1, 256(%2)\n" - "mtlr %0\n" - "mfxer %0\n" - "std %0, 296(%2)\n" - : "=&r" (tmp1), "=&r" (tmp2) - : "b" (newregs) - : "memory"); - } -#else else ppc_save_regs(newregs); -#endif /* __powerpc64__ */ } extern void kexec_smp_wait(void); /* get and clear naca physid, wait for -- cgit v1.2.3 From 7021d86afa6f3a8bf76218ac97f5847a6d985730 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 29 Dec 2008 06:40:35 +0000 Subject: powerpc/mm: Make clear_fixmap() actually work The clear_fixmap() routine issues map_page() with flags set to 0. Currently this causes a BUG_ON() inside the map_page(), as it assumes that a PTE should be clear before mapping. This patch makes the map_page() to trigger the BUG_ON() only if the flags were set. Signed-off-by: Anton Vorontsov Acked-by: Kumar Gala Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/pgtable_32.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/mm/pgtable_32.c b/arch/powerpc/mm/pgtable_32.c index 38ff35f2142a..22972cd83cc9 100644 --- a/arch/powerpc/mm/pgtable_32.c +++ b/arch/powerpc/mm/pgtable_32.c @@ -266,7 +266,8 @@ int map_page(unsigned long va, phys_addr_t pa, int flags) /* The PTE should never be already set nor present in the * hash table */ - BUG_ON(pte_val(*pg) & (_PAGE_PRESENT | _PAGE_HASHPTE)); + BUG_ON((pte_val(*pg) & (_PAGE_PRESENT | _PAGE_HASHPTE)) && + flags); set_pte_at(&init_mm, va, pg, pfn_pte(pa >> PAGE_SHIFT, __pgprot(flags))); } -- cgit v1.2.3 From 9b635642bce0500bdc7331ce8eeca97907b77117 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 6 Jan 2009 13:58:22 +0000 Subject: powerpc/cell: Fix the prototype of create_vma_map() Signed-off-by: Stephen Rothwell Acked-by: Arnd Bergmann Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/oprofile/cell/pr_util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/oprofile/cell/pr_util.h b/arch/powerpc/oprofile/cell/pr_util.h index 628009c01958..dfdbffa06818 100644 --- a/arch/powerpc/oprofile/cell/pr_util.h +++ b/arch/powerpc/oprofile/cell/pr_util.h @@ -79,7 +79,7 @@ struct spu_buffer { * the vma-to-fileoffset map. */ struct vma_to_fileoffset_map *create_vma_map(const struct spu *spu, - u64 objectid); + unsigned long objectid); unsigned int vma_map_lookup(struct vma_to_fileoffset_map *map, unsigned int vma, const struct spu *aSpu, int *grd_val); -- cgit v1.2.3 From 2b79d6962322facfd377a402730e4b381af95a40 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 6 Jan 2009 18:49:17 +0000 Subject: powerpc: enable dynamic ftrace This patch enables dynamic ftrace. The PowerPC port was dependent on other code not yet in mainline. Now that the code is, we can now let PowerPC compile with dynamic ftrace. Signed-off-by: Steven Rostedt Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index acda55199f55..84b861316ce7 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -108,6 +108,8 @@ config ARCH_NO_VIRT_TO_BUS config PPC bool default y + select HAVE_FTRACE_MCOUNT_RECORD + select HAVE_DYNAMIC_FTRACE select HAVE_FUNCTION_TRACER select ARCH_WANT_OPTIONAL_GPIOLIB select HAVE_IDE -- cgit v1.2.3 From 5886188dc7ba9a76babcd37452f44079a9a77f71 Mon Sep 17 00:00:00 2001 From: Benjamin Krill Date: Wed, 7 Jan 2009 10:32:38 +0100 Subject: serial: Add driver for the Cell Network Processor serial port NWP device Add support for the nwp serial device which is connected to a DCR bus. It uses the of_serial device driver to determine necessary properties from the device tree. The supported device is added as serial port number 85. NWP stands for network processor and it is part of the QPACE - Quantum Chromodynamics Parallel Computing on the Cell Broadband Engine project. The implementation is a lightweight uart implementation with the focus to consume as little resources as possible and it is connected to a DCR bus. Signed-off-by: Benjamin Krill Signed-off-by: Arnd Bergmann Signed-off-by: Andrew Morton Signed-off-by: Benjamin Herrenschmidt --- drivers/serial/Kconfig | 19 +- drivers/serial/Makefile | 1 + drivers/serial/nwpserial.c | 475 ++++++++++++++++++++++++++++++++++++++++++++ drivers/serial/of_serial.c | 19 ++ include/linux/nwpserial.h | 18 ++ include/linux/serial_core.h | 3 + 6 files changed, 534 insertions(+), 1 deletion(-) create mode 100644 drivers/serial/nwpserial.c create mode 100644 include/linux/nwpserial.h diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index b695ab3142d8..64fcf8c20821 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1320,13 +1320,30 @@ config SERIAL_NETX_CONSOLE config SERIAL_OF_PLATFORM tristate "Serial port on Open Firmware platform bus" depends on PPC_OF - depends on SERIAL_8250 + depends on SERIAL_8250 || SERIAL_OF_PLATFORM_NWPSERIAL help If you have a PowerPC based system that has serial ports on a platform specific bus, you should enable this option. Currently, only 8250 compatible ports are supported, but others can easily be added. +config SERIAL_OF_PLATFORM_NWPSERIAL + tristate "NWP serial port driver" + depends on PPC_OF && PPC_DCR + select SERIAL_OF_PLATFORM + select SERIAL_CORE_CONSOLE + select SERIAL_CORE + help + This driver supports the cell network processor nwp serial + device. + +config SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE + bool "Console on NWP serial port" + depends on SERIAL_OF_PLATFORM_NWPSERIAL=y + select SERIAL_CORE_CONSOLE + help + Support for Console on the NWP serial ports. + config SERIAL_QE tristate "Freescale QUICC Engine serial port support" depends on QUICC_ENGINE diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index dfe775ac45b2..8844c0a03929 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o +obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o diff --git a/drivers/serial/nwpserial.c b/drivers/serial/nwpserial.c new file mode 100644 index 000000000000..32f3eaf0d262 --- /dev/null +++ b/drivers/serial/nwpserial.c @@ -0,0 +1,475 @@ +/* + * Serial Port driver for a NWP uart device + * + * Copyright (C) 2008 IBM Corp., Benjamin Krill + * + * 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; either version + * 2 of the License, or (at your option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NWPSERIAL_NR 2 + +#define NWPSERIAL_STATUS_RXVALID 0x1 +#define NWPSERIAL_STATUS_TXFULL 0x2 + +struct nwpserial_port { + struct uart_port port; + dcr_host_t dcr_host; + unsigned int ier; + unsigned int mcr; +}; + +static DEFINE_MUTEX(nwpserial_mutex); +static struct nwpserial_port nwpserial_ports[NWPSERIAL_NR]; + +static void wait_for_bits(struct nwpserial_port *up, int bits) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = dcr_read(up->dcr_host, UART_LSR); + + if (--tmout == 0) + break; + udelay(1); + } while ((status & bits) != bits); +} + +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE +static void nwpserial_console_putchar(struct uart_port *port, int c) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + /* check if tx buffer is full */ + wait_for_bits(up, UART_LSR_THRE); + dcr_write(up->dcr_host, UART_TX, c); + up->port.icount.tx++; +} + +static void +nwpserial_console_write(struct console *co, const char *s, unsigned int count) +{ + struct nwpserial_port *up = &nwpserial_ports[co->index]; + unsigned long flags; + int locked = 1; + + if (oops_in_progress) + locked = spin_trylock_irqsave(&up->port.lock, flags); + else + spin_lock_irqsave(&up->port.lock, flags); + + /* save and disable interrupt */ + up->ier = dcr_read(up->dcr_host, UART_IER); + dcr_write(up->dcr_host, UART_IER, up->ier & ~UART_IER_RDI); + + uart_console_write(&up->port, s, count, nwpserial_console_putchar); + + /* wait for transmitter to become emtpy */ + while ((dcr_read(up->dcr_host, UART_LSR) & UART_LSR_THRE) == 0) + cpu_relax(); + + /* restore interrupt state */ + dcr_write(up->dcr_host, UART_IER, up->ier); + + if (locked) + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static struct uart_driver nwpserial_reg; +static struct console nwpserial_console = { + .name = "ttySQ", + .write = nwpserial_console_write, + .device = uart_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &nwpserial_reg, +}; +#define NWPSERIAL_CONSOLE (&nwpserial_console) +#else +#define NWPSERIAL_CONSOLE NULL +#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */ + +/**************************************************************************/ + +static int nwpserial_request_port(struct uart_port *port) +{ + return 0; +} + +static void nwpserial_release_port(struct uart_port *port) +{ + /* N/A */ +} + +static void nwpserial_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_NWPSERIAL; +} + +static irqreturn_t nwpserial_interrupt(int irq, void *dev_id) +{ + struct nwpserial_port *up = dev_id; + struct tty_struct *tty = up->port.info->port.tty; + irqreturn_t ret; + unsigned int iir; + unsigned char ch; + + spin_lock(&up->port.lock); + + /* check if the uart was the interrupt source. */ + iir = dcr_read(up->dcr_host, UART_IIR); + if (!iir) { + ret = IRQ_NONE; + goto out; + } + + do { + up->port.icount.rx++; + ch = dcr_read(up->dcr_host, UART_RX); + if (up->port.ignore_status_mask != NWPSERIAL_STATUS_RXVALID) + tty_insert_flip_char(tty, ch, TTY_NORMAL); + } while (dcr_read(up->dcr_host, UART_RX) & UART_LSR_DR); + + tty_flip_buffer_push(tty); + ret = IRQ_HANDLED; + +out: + spin_unlock(&up->port.lock); + return ret; +} + +static int nwpserial_startup(struct uart_port *port) +{ + struct nwpserial_port *up; + int err; + + up = container_of(port, struct nwpserial_port, port); + + /* disable flow control by default */ + up->mcr = dcr_read(up->dcr_host, UART_MCR) & ~UART_MCR_AFE; + dcr_write(up->dcr_host, UART_MCR, up->mcr); + + /* register interrupt handler */ + err = request_irq(up->port.irq, nwpserial_interrupt, + IRQF_SHARED, "nwpserial", up); + if (err) + return err; + + /* enable interrupts */ + up->ier = UART_IER_RDI; + dcr_write(up->dcr_host, UART_IER, up->ier); + + /* enable receiving */ + up->port.ignore_status_mask &= ~NWPSERIAL_STATUS_RXVALID; + + return 0; +} + +static void nwpserial_shutdown(struct uart_port *port) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + + /* disable receiving */ + up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID; + + /* disable interrupts from this port */ + up->ier = 0; + dcr_write(up->dcr_host, UART_IER, up->ier); + + /* free irq */ + free_irq(up->port.irq, port); +} + +static int nwpserial_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + return -EINVAL; +} + +static const char *nwpserial_type(struct uart_port *port) +{ + return port->type == PORT_NWPSERIAL ? "nwpserial" : NULL; +} + +static void nwpserial_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + + up->port.read_status_mask = NWPSERIAL_STATUS_RXVALID + | NWPSERIAL_STATUS_TXFULL; + + up->port.ignore_status_mask = 0; + /* ignore all characters if CREAD is not set */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID; + + /* Copy back the old hardware settings */ + if (old) + tty_termios_copy_hw(termios, old); +} + +static void nwpserial_break_ctl(struct uart_port *port, int ctl) +{ + /* N/A */ +} + +static void nwpserial_enable_ms(struct uart_port *port) +{ + /* N/A */ +} + +static void nwpserial_stop_rx(struct uart_port *port) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + /* don't forward any more data (like !CREAD) */ + up->port.ignore_status_mask = NWPSERIAL_STATUS_RXVALID; +} + +static void nwpserial_putchar(struct nwpserial_port *up, unsigned char c) +{ + /* check if tx buffer is full */ + wait_for_bits(up, UART_LSR_THRE); + dcr_write(up->dcr_host, UART_TX, c); + up->port.icount.tx++; +} + +static void nwpserial_start_tx(struct uart_port *port) +{ + struct nwpserial_port *up; + struct circ_buf *xmit; + up = container_of(port, struct nwpserial_port, port); + xmit = &up->port.info->xmit; + + if (port->x_char) { + nwpserial_putchar(up, up->port.x_char); + port->x_char = 0; + } + + while (!(uart_circ_empty(xmit) || uart_tx_stopped(&up->port))) { + nwpserial_putchar(up, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); + } +} + +static unsigned int nwpserial_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static void nwpserial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* N/A */ +} + +static void nwpserial_stop_tx(struct uart_port *port) +{ + /* N/A */ +} + +static unsigned int nwpserial_tx_empty(struct uart_port *port) +{ + struct nwpserial_port *up; + unsigned long flags; + int ret; + up = container_of(port, struct nwpserial_port, port); + + spin_lock_irqsave(&up->port.lock, flags); + ret = dcr_read(up->dcr_host, UART_LSR); + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret & UART_LSR_TEMT ? TIOCSER_TEMT : 0; +} + +static struct uart_ops nwpserial_pops = { + .tx_empty = nwpserial_tx_empty, + .set_mctrl = nwpserial_set_mctrl, + .get_mctrl = nwpserial_get_mctrl, + .stop_tx = nwpserial_stop_tx, + .start_tx = nwpserial_start_tx, + .stop_rx = nwpserial_stop_rx, + .enable_ms = nwpserial_enable_ms, + .break_ctl = nwpserial_break_ctl, + .startup = nwpserial_startup, + .shutdown = nwpserial_shutdown, + .set_termios = nwpserial_set_termios, + .type = nwpserial_type, + .release_port = nwpserial_release_port, + .request_port = nwpserial_request_port, + .config_port = nwpserial_config_port, + .verify_port = nwpserial_verify_port, +}; + +static struct uart_driver nwpserial_reg = { + .owner = THIS_MODULE, + .driver_name = "nwpserial", + .dev_name = "ttySQ", + .major = TTY_MAJOR, + .minor = 68, + .nr = NWPSERIAL_NR, + .cons = NWPSERIAL_CONSOLE, +}; + +int nwpserial_register_port(struct uart_port *port) +{ + struct nwpserial_port *up = NULL; + int ret = -1; + int i; + static int first = 1; + int dcr_len; + int dcr_base; + struct device_node *dn; + + mutex_lock(&nwpserial_mutex); + + dn = to_of_device(port->dev)->node; + if (dn == NULL) + goto out; + + /* get dcr base. */ + dcr_base = dcr_resource_start(dn, 0); + + /* find matching entry */ + for (i = 0; i < NWPSERIAL_NR; i++) + if (nwpserial_ports[i].port.iobase == dcr_base) { + up = &nwpserial_ports[i]; + break; + } + + /* we didn't find a mtching entry, search for a free port */ + if (up == NULL) + for (i = 0; i < NWPSERIAL_NR; i++) + if (nwpserial_ports[i].port.type == PORT_UNKNOWN && + nwpserial_ports[i].port.iobase == 0) { + up = &nwpserial_ports[i]; + break; + } + + if (up == NULL) { + ret = -EBUSY; + goto out; + } + + if (first) + uart_register_driver(&nwpserial_reg); + first = 0; + + up->port.membase = port->membase; + up->port.irq = port->irq; + up->port.uartclk = port->uartclk; + up->port.fifosize = port->fifosize; + up->port.regshift = port->regshift; + up->port.iotype = port->iotype; + up->port.flags = port->flags; + up->port.mapbase = port->mapbase; + up->port.private_data = port->private_data; + + if (port->dev) + up->port.dev = port->dev; + + if (up->port.iobase != dcr_base) { + up->port.ops = &nwpserial_pops; + up->port.fifosize = 16; + + spin_lock_init(&up->port.lock); + + up->port.iobase = dcr_base; + dcr_len = dcr_resource_len(dn, 0); + + up->dcr_host = dcr_map(dn, dcr_base, dcr_len); + if (!DCR_MAP_OK(up->dcr_host)) { + printk(KERN_ERR "Cannot map DCR resources for NWPSERIAL"); + goto out; + } + } + + ret = uart_add_one_port(&nwpserial_reg, &up->port); + if (ret == 0) + ret = up->port.line; + +out: + mutex_unlock(&nwpserial_mutex); + + return ret; +} +EXPORT_SYMBOL(nwpserial_register_port); + +void nwpserial_unregister_port(int line) +{ + struct nwpserial_port *up = &nwpserial_ports[line]; + mutex_lock(&nwpserial_mutex); + uart_remove_one_port(&nwpserial_reg, &up->port); + + up->port.type = PORT_UNKNOWN; + + mutex_unlock(&nwpserial_mutex); +} +EXPORT_SYMBOL(nwpserial_unregister_port); + +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE +static int __init nwpserial_console_init(void) +{ + struct nwpserial_port *up = NULL; + struct device_node *dn; + const char *name; + int dcr_base; + int dcr_len; + int i; + + /* search for a free port */ + for (i = 0; i < NWPSERIAL_NR; i++) + if (nwpserial_ports[i].port.type == PORT_UNKNOWN) { + up = &nwpserial_ports[i]; + break; + } + + if (up == NULL) + return -1; + + name = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (name == NULL) + return -1; + + dn = of_find_node_by_path(name); + if (!dn) + return -1; + + spin_lock_init(&up->port.lock); + up->port.ops = &nwpserial_pops; + up->port.type = PORT_NWPSERIAL; + up->port.fifosize = 16; + + dcr_base = dcr_resource_start(dn, 0); + dcr_len = dcr_resource_len(dn, 0); + up->port.iobase = dcr_base; + + up->dcr_host = dcr_map(dn, dcr_base, dcr_len); + if (!DCR_MAP_OK(up->dcr_host)) { + printk("Cannot map DCR resources for SERIAL"); + return -1; + } + register_console(&nwpserial_console); + return 0; +} +console_initcall(nwpserial_console_init); +#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */ diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c index 8fa0ff561e9f..a821e3a3d664 100644 --- a/drivers/serial/of_serial.c +++ b/drivers/serial/of_serial.c @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -99,9 +100,16 @@ static int __devinit of_platform_serial_probe(struct of_device *ofdev, goto out; switch (port_type) { +#ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: ret = serial8250_register_port(&port); break; +#endif +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL + case PORT_NWPSERIAL: + ret = nwpserial_register_port(&port); + break; +#endif default: /* need to add code for these */ case PORT_UNKNOWN: @@ -129,9 +137,16 @@ static int of_platform_serial_remove(struct of_device *ofdev) { struct of_serial_info *info = ofdev->dev.driver_data; switch (info->type) { +#ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: serial8250_unregister_port(info->line); break; +#endif +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL + case PORT_NWPSERIAL: + nwpserial_unregister_port(info->line); + break; +#endif default: /* need to add code for these */ break; @@ -148,6 +163,10 @@ static struct of_device_id __devinitdata of_platform_serial_table[] = { { .type = "serial", .compatible = "ns16450", .data = (void *)PORT_16450, }, { .type = "serial", .compatible = "ns16550", .data = (void *)PORT_16550, }, { .type = "serial", .compatible = "ns16750", .data = (void *)PORT_16750, }, +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL + { .type = "serial", .compatible = "ibm,qpace-nwp-serial", + .data = (void *)PORT_NWPSERIAL, }, +#endif { .type = "serial", .data = (void *)PORT_UNKNOWN, }, { /* end of list */ }, }; diff --git a/include/linux/nwpserial.h b/include/linux/nwpserial.h new file mode 100644 index 000000000000..9acb21572eaf --- /dev/null +++ b/include/linux/nwpserial.h @@ -0,0 +1,18 @@ +/* + * Serial Port driver for a NWP uart device + * + * Copyright (C) 2008 IBM Corp., Benjamin Krill + * + * 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; either version + * 2 of the License, or (at your option) any later version. + * + */ +#ifndef _NWPSERIAL_H +#define _NWPSERIAL_H + +int nwpserial_register_port(struct uart_port *port); +void nwpserial_unregister_port(int line); + +#endif /* _NWPSERIAL_H */ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index b4199841f1fc..90bbbf0b1161 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -161,6 +161,9 @@ #define PORT_S3C6400 84 +/* NWPSERIAL */ +#define PORT_NWPSERIAL 85 + #ifdef __KERNEL__ #include -- cgit v1.2.3