diff options
author | ian <ian@FreeBSD.org> | 2014-05-15 15:43:33 +0000 |
---|---|---|
committer | ian <ian@FreeBSD.org> | 2014-05-15 15:43:33 +0000 |
commit | 69ca53fdd27132e6f0401bc4df990d8dafad71ca (patch) | |
tree | 2b91e4015d8b11324119827d2065ff5f9f09c584 /sys/arm | |
parent | dcc5cc2e24c7d9e6e059f99797df7acb6c77633e (diff) | |
download | FreeBSD-src-69ca53fdd27132e6f0401bc4df990d8dafad71ca.zip FreeBSD-src-69ca53fdd27132e6f0401bc4df990d8dafad71ca.tar.gz |
MFC r261406, r261409: enhance Vybrid support...
o Expand device tree information
o Export iomuxc (pins) configuration to DTS
o Allow devices to assign clocks in DTS
o Split kernel configuration to chip common and board specific parts.
Diffstat (limited to 'sys/arm')
-rw-r--r-- | sys/arm/conf/COSMIC | 116 | ||||
-rw-r--r-- | sys/arm/conf/VYBRID.common | 139 | ||||
-rw-r--r-- | sys/arm/freescale/vybrid/vf_ccm.c | 311 | ||||
-rw-r--r-- | sys/arm/freescale/vybrid/vf_iomuxc.c | 74 |
4 files changed, 499 insertions, 141 deletions
diff --git a/sys/arm/conf/COSMIC b/sys/arm/conf/COSMIC index 27e028c..464d852 100644 --- a/sys/arm/conf/COSMIC +++ b/sys/arm/conf/COSMIC @@ -17,123 +17,9 @@ # # $FreeBSD$ +include "VYBRID.common" ident COSMIC -include "../freescale/vybrid/std.vybrid" - -makeoptions MODULES_OVERRIDE="" -makeoptions WITHOUT_MODULES="ahc" - -makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols -makeoptions WERROR="-Werror" - -options HZ=100 ## -options SCHED_4BSD #4BSD scheduler -options INET #InterNETworking -options INET6 #IPv6 communications protocols -options FFS #Berkeley Fast Filesystem -options SOFTUPDATES -options UFS_ACL #Support for access control lists -options UFS_DIRHASH #Improve performance on big directories -options MSDOSFS #MSDOS Filesystem -options CD9660 #ISO 9660 Filesystem -options PROCFS #Process filesystem (requires PSEUDOFS) -options PSEUDOFS #Pseudo-filesystem framework -#options NANDFS #NAND Filesystem -options TMPFS -options COMPAT_43 #Compatible with BSD 4.3 [KEEP THIS!] -options SCSI_DELAY=5000 #Delay (in ms) before probing SCSI -options KTRACE -options SYSVSHM #SYSV-style shared memory -options SYSVMSG #SYSV-style message queues -options SYSVSEM #SYSV-style semaphores -options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions -options KBD_INSTALL_CDEV -options PREEMPTION -options FREEBSD_BOOT_LOADER -options VFP # vfp/neon - -# Debugging -makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols -options BREAK_TO_DEBUGGER -#options VERBOSE_SYSINIT #Enable verbose sysinit messages -options KDB -options DDB #Enable the kernel debugger -options INVARIANTS #Enable calls of extra sanity checking -options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS -#options WITNESS #Enable checks to detect deadlocks and cycles -#options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed -options DIAGNOSTIC - -# NFS support -options NFSCL #Network Filesystem Client -options NFSLOCKD #Network Lock Manager -options NFS_ROOT #NFS usable as /, requires NFSCLIENT - -# Uncomment this for NFS root -#options NFS_ROOT #NFS usable as /, requires NFSCL -#options BOOTP_NFSROOT -#options BOOTP_COMPAT -#options BOOTP -#options BOOTP_NFSV3 -#options BOOTP_WIRED_TO=ffec0 - -device mmc # mmc/sd bus -device mmcsd # mmc/sd flash cards -device sdhci # generic sdhci - -#options ROOTDEVNAME=\"nfs:10.5.0.1:/tftpboot/cosmic\" -#options ROOTDEVNAME=\"nandfs:/dev/gnand0s.root\" -options ROOTDEVNAME=\"ufs:/dev/da0\" - -#options SMP - -# Pseudo devices - -device loop -device random -device pty -device md -device gpio - -# USB support -options USB_HOST_ALIGN=32 # Align usb buffers to cache line size. -device usb -options USB_DEBUG -#options USB_REQ_DEBUG -#options USB_VERBOSE -#device musb -device ehci -#device ohci - -device umass -device scbus # SCSI bus (required for SCSI) -device da # Direct Access (disks) -device pass - -# SATA -#device ata -#device atadisk -#device mvs - -device nand - -# Serial ports -device uart - -# I2C (TWSI) -#device iic -#device iicbus - -# Ethernet -device ether -device ffec - -# USB ethernet support, requires miibus -device miibus -device axe # ASIX Electronics USB Ethernet -device bpf # Berkeley packet filter - #FDT options FDT options FDT_DTB_STATIC diff --git a/sys/arm/conf/VYBRID.common b/sys/arm/conf/VYBRID.common new file mode 100644 index 0000000..78c93be --- /dev/null +++ b/sys/arm/conf/VYBRID.common @@ -0,0 +1,139 @@ +# Kernel configuration for Vybrid Family boards. +# +# For more information on this file, please read the config(5) manual page, +# and/or the handbook section on Kernel Configuration Files: +# +# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html +# +# The handbook is also available locally in /usr/share/doc/handbook +# if you've installed the doc distribution, otherwise always see the +# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the +# latest information. +# +# An exhaustive list of options and more detailed explanations of the +# device lines is also present in the ../../conf/NOTES and NOTES files. +# If you are in doubt as to the purpose or necessity of a line, check first +# in NOTES. +# +# $FreeBSD$ + +include "../freescale/vybrid/std.vybrid" + +makeoptions MODULES_OVERRIDE="" +makeoptions WITHOUT_MODULES="ahc" + +makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols +makeoptions WERROR="-Werror" + +options HZ=100 ## +options SCHED_4BSD #4BSD scheduler +options INET #InterNETworking +options INET6 #IPv6 communications protocols +options FFS #Berkeley Fast Filesystem +options SOFTUPDATES +options UFS_ACL #Support for access control lists +options UFS_DIRHASH #Improve performance on big directories +options MSDOSFS #MSDOS Filesystem +options CD9660 #ISO 9660 Filesystem +options PROCFS #Process filesystem (requires PSEUDOFS) +options PSEUDOFS #Pseudo-filesystem framework +#options NANDFS #NAND Filesystem +options TMPFS +options COMPAT_43 #Compatible with BSD 4.3 [KEEP THIS!] +options SCSI_DELAY=5000 #Delay (in ms) before probing SCSI +options KTRACE +options SYSVSHM #SYSV-style shared memory +options SYSVMSG #SYSV-style message queues +options SYSVSEM #SYSV-style semaphores +options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions +options KBD_INSTALL_CDEV +options PREEMPTION +options FREEBSD_BOOT_LOADER +options MUTEX_NOINLINE +options RWLOCK_NOINLINE +options NO_FFS_SNAPSHOT +options NO_SWAPPING +options VFP # vfp/neon + +# Debugging +makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols +options BREAK_TO_DEBUGGER +#options VERBOSE_SYSINIT #Enable verbose sysinit messages +options KDB +options DDB #Enable the kernel debugger +options INVARIANTS #Enable calls of extra sanity checking +options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS +#options WITNESS #Enable checks to detect deadlocks and cycles +#options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed +options DIAGNOSTIC + +# NFS support +options NFSCL #Network Filesystem Client +options NFSLOCKD #Network Lock Manager +options NFS_ROOT #NFS usable as /, requires NFSCLIENT + +# Uncomment this for NFS root +#options NFS_ROOT #NFS usable as /, requires NFSCL +#options BOOTP_NFSROOT +#options BOOTP_COMPAT +#options BOOTP +#options BOOTP_NFSV3 +#options BOOTP_WIRED_TO=ffec0 + +device mmc # mmc/sd bus +device mmcsd # mmc/sd flash cards +device sdhci # generic sdhci + +#options ROOTDEVNAME=\"nfs:10.5.0.1:/tftpboot/cosmic\" +#options ROOTDEVNAME=\"nandfs:/dev/gnand0s.root\" +options ROOTDEVNAME=\"ufs:/dev/da0\" + +#options SMP + +# Pseudo devices + +device loop +device random +device pty +device md +device gpio + +# USB support +options USB_HOST_ALIGN=32 # Align usb buffers to cache line size. +device usb +options USB_DEBUG +#options USB_REQ_DEBUG +#options USB_VERBOSE +#device musb +device ehci +#device ohci + +device umass +device scbus # SCSI bus (required for SCSI) +device da # Direct Access (disks) +device pass + +# SATA +#device ata +#device atadisk +#device mvs + +device nand + +# Serial ports +device uart + +# I2C (TWSI) +#device iic +#device iicbus + +# Ethernet +device ether +device ffec + +# USB ethernet support, requires miibus +device miibus +device axe # ASIX Electronics USB Ethernet +device bpf # Berkeley packet filter + +device sound diff --git a/sys/arm/freescale/vybrid/vf_ccm.c b/sys/arm/freescale/vybrid/vf_ccm.c index 1410faa..ddcaaae 100644 --- a/sys/arm/freescale/vybrid/vf_ccm.c +++ b/sys/arm/freescale/vybrid/vf_ccm.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com> + * Copyright (c) 2013-2014 Ruslan Bukin <br@bsdpad.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -73,8 +73,8 @@ __FBSDID("$FreeBSD$"); #define CCM_CCGRN 12 #define CCM_CCGR(n) (0x40 + (n * 0x04)) /* Clock Gating Register */ -#define CCM_CMEOR(n) (0x70 + (n * 0x70)) /* Module Enable Override Reg */ -#define CCM_CCPGR(n) (0x90 + (n * 0x04)) /* Platform Clock Gating Reg */ +#define CCM_CMEOR(n) (0x70 + (n * 0x70)) /* Module Enable Override */ +#define CCM_CCPGR(n) (0x90 + (n * 0x04)) /* Platform Clock Gating */ #define CCM_CPPDSR 0x88 /* PLL PFD Disable Status Register */ #define CCM_CCOWR 0x8C /* CORE Wakeup Register */ @@ -100,6 +100,228 @@ __FBSDID("$FreeBSD$"); /* CCM_CSCDR1 */ #define ENET_TS_EN (1 << 23) #define RMII_CLK_EN (1 << 24) +#define SAI3_EN (1 << 19) + +/* CCM_CSCDR2 */ +#define ESAI_EN (1 << 30) +#define ESDHC1_EN (1 << 29) +#define ESDHC0_EN (1 << 28) +#define NFC_EN (1 << 9) +#define ESDHC1_DIV_S 20 +#define ESDHC1_DIV_M 0xf +#define ESDHC0_DIV_S 16 +#define ESDHC0_DIV_M 0xf + +/* CCM_CSCDR3 */ +#define DCU0_EN (1 << 19) + +#define QSPI1_EN (1 << 12) +#define QSPI1_DIV (1 << 11) +#define QSPI1_X2_DIV (1 << 10) +#define QSPI1_X4_DIV_M 0x3 +#define QSPI1_X4_DIV_S 8 + +#define QSPI0_EN (1 << 4) +#define QSPI0_DIV (1 << 3) +#define QSPI0_X2_DIV (1 << 2) +#define QSPI0_X4_DIV_M 0x3 +#define QSPI0_X4_DIV_S 0 + +#define SAI3_DIV_SHIFT 12 +#define SAI3_DIV_MASK 0xf +#define ESAI_DIV_SHIFT 24 +#define ESAI_DIV_MASK 0xf + +#define PLL4_CLK_DIV_SHIFT 6 +#define PLL4_CLK_DIV_MASK 0x7 + +#define IPG_CLK_DIV_SHIFT 11 +#define IPG_CLK_DIV_MASK 0x3 + +#define ESAI_CLK_SEL_SHIFT 20 +#define ESAI_CLK_SEL_MASK 0x3 + +#define SAI3_CLK_SEL_SHIFT 6 +#define SAI3_CLK_SEL_MASK 0x3 + +#define CKO1_EN (1 << 10) +#define CKO1_DIV_MASK 0xf +#define CKO1_DIV_SHIFT 6 +#define CKO1_SEL_MASK 0x3f +#define CKO1_SEL_SHIFT 0 +#define CKO1_PLL4_MAIN 0x6 +#define CKO1_PLL4_DIVD 0x7 + +struct clk { + uint32_t reg; + uint32_t enable_reg; + uint32_t div_mask; + uint32_t div_shift; + uint32_t div_val; + uint32_t sel_reg; + uint32_t sel_mask; + uint32_t sel_shift; + uint32_t sel_val; +}; + +/* + PLL4 clock divider (before switching the clocks should be gated) + 000 Divide by 1 (only if PLL frequency less than or equal to 650 MHz) + 001 Divide by 4 + 010 Divide by 6 + 011 Divide by 8 + 100 Divide by 10 + 101 Divide by 12 + 110 Divide by 14 + 111 Divide by 16 +*/ + +static struct clk pll4_clk = { + .reg = CCM_CACRR, + .enable_reg = 0, + .div_mask = PLL4_CLK_DIV_MASK, + .div_shift = PLL4_CLK_DIV_SHIFT, + .div_val = 5, /* Divide by 12 */ + .sel_reg = 0, + .sel_mask = 0, + .sel_shift = 0, + .sel_val = 0, +}; + +static struct clk sai3_clk = { + .reg = CCM_CSCDR1, + .enable_reg = SAI3_EN, + .div_mask = SAI3_DIV_MASK, + .div_shift = SAI3_DIV_SHIFT, + .div_val = 1, + .sel_reg = CCM_CSCMR1, + .sel_mask = SAI3_CLK_SEL_MASK, + .sel_shift = SAI3_CLK_SEL_SHIFT, + .sel_val = 0x3, /* Divided PLL4 main clock */ +}; + +static struct clk cko1_clk = { + .reg = CCM_CCOSR, + .enable_reg = CKO1_EN, + .div_mask = CKO1_DIV_MASK, + .div_shift = CKO1_DIV_SHIFT, + .div_val = 1, + .sel_reg = CCM_CCOSR, + .sel_mask = CKO1_SEL_MASK, + .sel_shift = CKO1_SEL_SHIFT, + .sel_val = CKO1_PLL4_DIVD, +}; + +static struct clk esdhc0_clk = { + .reg = CCM_CSCDR2, + .enable_reg = ESDHC0_EN, + .div_mask = ESDHC0_DIV_M, + .div_shift = ESDHC0_DIV_S, + .div_val = 0x9, + .sel_reg = 0, + .sel_mask = 0, + .sel_shift = 0, + .sel_val = 0, +}; + +static struct clk esdhc1_clk = { + .reg = CCM_CSCDR2, + .enable_reg = ESDHC1_EN, + .div_mask = ESDHC1_DIV_M, + .div_shift = ESDHC1_DIV_S, + .div_val = 0x9, + .sel_reg = 0, + .sel_mask = 0, + .sel_shift = 0, + .sel_val = 0, +}; + +static struct clk qspi0_clk = { + .reg = CCM_CSCDR3, + .enable_reg = QSPI0_EN, + .div_mask = 0, + .div_shift = 0, + .div_val = 0, + .sel_reg = 0, + .sel_mask = 0, + .sel_shift = 0, + .sel_val = 0, +}; + +static struct clk dcu0_clk = { + .reg = CCM_CSCDR3, + .enable_reg = DCU0_EN, + .div_mask = 0x7, + .div_shift = 16, /* DCU0_DIV */ + .div_val = 0, /* divide by 1 */ + .sel_reg = 0, + .sel_mask = 0, + .sel_shift = 0, + .sel_val = 0, +}; + +static struct clk enet_clk = { + .reg = CCM_CSCDR1, + .enable_reg = (ENET_TS_EN | RMII_CLK_EN), + .div_mask = 0, + .div_shift = 0, + .div_val = 0, + .sel_reg = 0, + .sel_mask = 0, + .sel_shift = 0, + .sel_val = 0, +}; + +static struct clk nand_clk = { + .reg = CCM_CSCDR2, + .enable_reg = NFC_EN, + .div_mask = 0, + .div_shift = 0, + .div_val = 0, + .sel_reg = 0, + .sel_mask = 0, + .sel_shift = 0, + .sel_val = 0, +}; + +/* + Divider to generate ESAI clock + 0000 Divide by 1 + 0001 Divide by 2 + ... ... + 1111 Divide by 16 +*/ + +static struct clk esai_clk = { + .reg = CCM_CSCDR2, + .enable_reg = ESAI_EN, + .div_mask = ESAI_DIV_MASK, + .div_shift = ESAI_DIV_SHIFT, + .div_val = 3, /* Divide by 4 */ + .sel_reg = CCM_CSCMR1, + .sel_mask = ESAI_CLK_SEL_MASK, + .sel_shift = ESAI_CLK_SEL_SHIFT, + .sel_val = 0x3, /* Divided PLL4 main clock */ +}; + +struct clock_entry { + char *name; + struct clk *clk; +}; + +static struct clock_entry clock_map[] = { + {"pll4", &pll4_clk}, + {"sai3", &sai3_clk}, + {"cko1", &cko1_clk}, + {"esdhc0", &esdhc0_clk}, + {"esdhc1", &esdhc1_clk}, + {"qspi0", &qspi0_clk}, + {"dcu0", &dcu0_clk}, + {"enet", &enet_clk}, + {"nand", &nand_clk}, + {"esai", &esai_clk}, + {NULL, NULL} +}; struct ccm_softc { struct resource *res[1]; @@ -125,6 +347,83 @@ ccm_probe(device_t dev) } static int +set_clock(struct ccm_softc *sc, char *name) +{ + struct clk *clk; + int reg; + int i; + + for (i = 0; clock_map[i].name != NULL; i++) { + if (strcmp(clock_map[i].name, name) == 0) { +#if 0 + device_printf(sc->dev, "Configuring %s clk\n", name); +#endif + clk = clock_map[i].clk; + if (clk->sel_reg != 0) { + reg = READ4(sc, clk->sel_reg); + reg &= ~(clk->sel_mask << clk->sel_shift); + reg |= (clk->sel_val << clk->sel_shift); + WRITE4(sc, clk->sel_reg, reg); + }; + + reg = READ4(sc, clk->reg); + reg |= clk->enable_reg; + reg &= ~(clk->div_mask << clk->div_shift); + reg |= (clk->div_val << clk->div_shift); + WRITE4(sc, clk->reg, reg); + }; + }; + + return (0); +} + +static int +ccm_fdt_set(struct ccm_softc *sc) +{ + phandle_t child, parent, root; + int len; + char *fdt_config, *name; + + root = OF_finddevice("/"); + len = 0; + parent = root; + + /* Find 'clock_names' prop in the tree */ + for (child = OF_child(parent); child != 0; child = OF_peer(child)) { + + /* Find a 'leaf'. Start the search from this node. */ + while (OF_child(child)) { + parent = child; + child = OF_child(child); + } + + if (!fdt_is_enabled(child)) + continue; + + if ((len = OF_getproplen(child, "clock_names")) > 0) { + len = OF_getproplen(child, "clock_names"); + OF_getprop_alloc(child, "clock_names", 1, + (void **)&fdt_config); + + while (len > 0) { + name = fdt_config; + fdt_config += strlen(name) + 1; + len -= strlen(name) + 1; + set_clock(sc, name); + }; + }; + + if (OF_peer(child) == 0) { + /* No more siblings. */ + child = parent; + parent = OF_parent(child); + } + } + + return (0); +} + +static int ccm_attach(device_t dev) { struct ccm_softc *sc; @@ -163,10 +462,8 @@ ccm_attach(device_t dev) WRITE4(sc, CCM_CCGR(i), 0xffffffff); } - /* Enable ENET clocks */ - reg = READ4(sc, CCM_CSCDR1); - reg |= (ENET_TS_EN | RMII_CLK_EN); - WRITE4(sc, CCM_CSCDR1, reg); + /* Take and apply FDT clocks */ + ccm_fdt_set(sc); return (0); } diff --git a/sys/arm/freescale/vybrid/vf_iomuxc.c b/sys/arm/freescale/vybrid/vf_iomuxc.c index 08daf5e..20b1fd1 100644 --- a/sys/arm/freescale/vybrid/vf_iomuxc.c +++ b/sys/arm/freescale/vybrid/vf_iomuxc.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com> + * Copyright (c) 2013-2014 Ruslan Bukin <br@bsdpad.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -67,16 +67,12 @@ __FBSDID("$FreeBSD$"); #define MUX_MODE_MASK 7 #define MUX_MODE_SHIFT 20 #define MUX_MODE_GPIO 0 -#define MUX_MODE_RMII 1 -#define MUX_MODE_RMII_CLKIN 2 #define MUX_MODE_VBUS_EN_OTG 2 #define PUS_22_KOHM_PULL_UP (3 << 4) #define DSE_25_OHM (6 << 6) -#define NET0_PAD_START 45 -#define NET1_PAD_START 54 -#define NET_PAD_N 9 +#define MAX_MUX_LEN 1024 struct iomuxc_softc { struct resource *tmr_res[1]; @@ -115,11 +111,62 @@ configure_pad(struct iomuxc_softc *sc, int pad, int mux_mode) } static int +pinmux_set(struct iomuxc_softc *sc) +{ + phandle_t child, parent, root; + pcell_t iomux_config[MAX_MUX_LEN]; + int len; + int values; + int pin; + int mux_mode; + int i; + + root = OF_finddevice("/"); + len = 0; + parent = root; + + /* Find 'iomux_config' prop in the nodes */ + for (child = OF_child(parent); child != 0; child = OF_peer(child)) { + + /* Find a 'leaf'. Start the search from this node. */ + while (OF_child(child)) { + parent = child; + child = OF_child(child); + } + + if (!fdt_is_enabled(child)) + continue; + + if ((len = OF_getproplen(child, "iomux_config")) > 0) { + OF_getprop(child, "iomux_config", &iomux_config, len); + + values = len / (sizeof(uint32_t)); + for (i = 0; i < values; i += 2) { + pin = fdt32_to_cpu(iomux_config[i]); + mux_mode = fdt32_to_cpu(iomux_config[i+1]); +#if 0 + device_printf(sc->dev, "Set pin %d to ALT%d\n", + pin, mux_mode); +#endif + configure_pad(sc, IOMUXC(pin), mux_mode); + } + } + + if (OF_peer(child) == 0) { + /* No more siblings. */ + child = parent; + parent = OF_parent(child); + } + } + + return (0); +} + +static int iomuxc_attach(device_t dev) { struct iomuxc_softc *sc; int reg; - int i; sc = device_get_softc(dev); sc->dev = dev; @@ -138,18 +185,7 @@ iomuxc_attach(device_t dev) reg = (PKE | PUE | PUS_22_KOHM_PULL_UP | DSE_25_OHM | OBE); WRITE4(sc, IOMUXC_PTA7, reg); - /* NET */ - configure_pad(sc, IOMUXC_PTA6, MUX_MODE_RMII_CLKIN); - - /* NET0 */ - for (i = NET0_PAD_START; i <= (NET0_PAD_START + NET_PAD_N); i++) { - configure_pad(sc, IOMUXC(i), MUX_MODE_RMII); - } - - /* NET1 */ - for (i = NET1_PAD_START; i <= (NET1_PAD_START + NET_PAD_N); i++) { - configure_pad(sc, IOMUXC(i), MUX_MODE_RMII); - } + pinmux_set(sc); return (0); } |