Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Support for the OLPC DCON and OLPC EC access
0004  *
0005  * Copyright © 2006  Advanced Micro Devices, Inc.
0006  * Copyright © 2007-2008  Andres Salomon <dilinger@debian.org>
0007  */
0008 
0009 #include <linux/kernel.h>
0010 #include <linux/init.h>
0011 #include <linux/export.h>
0012 #include <linux/delay.h>
0013 #include <linux/io.h>
0014 #include <linux/string.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/of.h>
0017 #include <linux/syscore_ops.h>
0018 #include <linux/mutex.h>
0019 #include <linux/olpc-ec.h>
0020 
0021 #include <asm/geode.h>
0022 #include <asm/setup.h>
0023 #include <asm/olpc.h>
0024 #include <asm/olpc_ofw.h>
0025 
0026 struct olpc_platform_t olpc_platform_info;
0027 EXPORT_SYMBOL_GPL(olpc_platform_info);
0028 
0029 /* what the timeout *should* be (in ms) */
0030 #define EC_BASE_TIMEOUT 20
0031 
0032 /* the timeout that bugs in the EC might force us to actually use */
0033 static int ec_timeout = EC_BASE_TIMEOUT;
0034 
0035 static int __init olpc_ec_timeout_set(char *str)
0036 {
0037     if (get_option(&str, &ec_timeout) != 1) {
0038         ec_timeout = EC_BASE_TIMEOUT;
0039         printk(KERN_ERR "olpc-ec:  invalid argument to "
0040                 "'olpc_ec_timeout=', ignoring!\n");
0041     }
0042     printk(KERN_DEBUG "olpc-ec:  using %d ms delay for EC commands.\n",
0043             ec_timeout);
0044     return 1;
0045 }
0046 __setup("olpc_ec_timeout=", olpc_ec_timeout_set);
0047 
0048 /*
0049  * These {i,o}bf_status functions return whether the buffers are full or not.
0050  */
0051 
0052 static inline unsigned int ibf_status(unsigned int port)
0053 {
0054     return !!(inb(port) & 0x02);
0055 }
0056 
0057 static inline unsigned int obf_status(unsigned int port)
0058 {
0059     return inb(port) & 0x01;
0060 }
0061 
0062 #define wait_on_ibf(p, d) __wait_on_ibf(__LINE__, (p), (d))
0063 static int __wait_on_ibf(unsigned int line, unsigned int port, int desired)
0064 {
0065     unsigned int timeo;
0066     int state = ibf_status(port);
0067 
0068     for (timeo = ec_timeout; state != desired && timeo; timeo--) {
0069         mdelay(1);
0070         state = ibf_status(port);
0071     }
0072 
0073     if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
0074             timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
0075         printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for IBF!\n",
0076                 line, ec_timeout - timeo);
0077     }
0078 
0079     return !(state == desired);
0080 }
0081 
0082 #define wait_on_obf(p, d) __wait_on_obf(__LINE__, (p), (d))
0083 static int __wait_on_obf(unsigned int line, unsigned int port, int desired)
0084 {
0085     unsigned int timeo;
0086     int state = obf_status(port);
0087 
0088     for (timeo = ec_timeout; state != desired && timeo; timeo--) {
0089         mdelay(1);
0090         state = obf_status(port);
0091     }
0092 
0093     if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
0094             timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
0095         printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for OBF!\n",
0096                 line, ec_timeout - timeo);
0097     }
0098 
0099     return !(state == desired);
0100 }
0101 
0102 /*
0103  * This allows the kernel to run Embedded Controller commands.  The EC is
0104  * documented at <http://wiki.laptop.org/go/Embedded_controller>, and the
0105  * available EC commands are here:
0106  * <http://wiki.laptop.org/go/Ec_specification>.  Unfortunately, while
0107  * OpenFirmware's source is available, the EC's is not.
0108  */
0109 static int olpc_xo1_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *outbuf,
0110         size_t outlen, void *arg)
0111 {
0112     int ret = -EIO;
0113     int i;
0114     int restarts = 0;
0115 
0116     /* Clear OBF */
0117     for (i = 0; i < 10 && (obf_status(0x6c) == 1); i++)
0118         inb(0x68);
0119     if (i == 10) {
0120         printk(KERN_ERR "olpc-ec:  timeout while attempting to "
0121                 "clear OBF flag!\n");
0122         goto err;
0123     }
0124 
0125     if (wait_on_ibf(0x6c, 0)) {
0126         printk(KERN_ERR "olpc-ec:  timeout waiting for EC to "
0127                 "quiesce!\n");
0128         goto err;
0129     }
0130 
0131 restart:
0132     /*
0133      * Note that if we time out during any IBF checks, that's a failure;
0134      * we have to return.  There's no way for the kernel to clear that.
0135      *
0136      * If we time out during an OBF check, we can restart the command;
0137      * reissuing it will clear the OBF flag, and we should be alright.
0138      * The OBF flag will sometimes misbehave due to what we believe
0139      * is a hardware quirk..
0140      */
0141     pr_devel("olpc-ec:  running cmd 0x%x\n", cmd);
0142     outb(cmd, 0x6c);
0143 
0144     if (wait_on_ibf(0x6c, 0)) {
0145         printk(KERN_ERR "olpc-ec:  timeout waiting for EC to read "
0146                 "command!\n");
0147         goto err;
0148     }
0149 
0150     if (inbuf && inlen) {
0151         /* write data to EC */
0152         for (i = 0; i < inlen; i++) {
0153             pr_devel("olpc-ec:  sending cmd arg 0x%x\n", inbuf[i]);
0154             outb(inbuf[i], 0x68);
0155             if (wait_on_ibf(0x6c, 0)) {
0156                 printk(KERN_ERR "olpc-ec:  timeout waiting for"
0157                         " EC accept data!\n");
0158                 goto err;
0159             }
0160         }
0161     }
0162     if (outbuf && outlen) {
0163         /* read data from EC */
0164         for (i = 0; i < outlen; i++) {
0165             if (wait_on_obf(0x6c, 1)) {
0166                 printk(KERN_ERR "olpc-ec:  timeout waiting for"
0167                         " EC to provide data!\n");
0168                 if (restarts++ < 10)
0169                     goto restart;
0170                 goto err;
0171             }
0172             outbuf[i] = inb(0x68);
0173             pr_devel("olpc-ec:  received 0x%x\n", outbuf[i]);
0174         }
0175     }
0176 
0177     ret = 0;
0178 err:
0179     return ret;
0180 }
0181 
0182 static bool __init check_ofw_architecture(struct device_node *root)
0183 {
0184     const char *olpc_arch;
0185     int propsize;
0186 
0187     olpc_arch = of_get_property(root, "architecture", &propsize);
0188     return propsize == 5 && strncmp("OLPC", olpc_arch, 5) == 0;
0189 }
0190 
0191 static u32 __init get_board_revision(struct device_node *root)
0192 {
0193     int propsize;
0194     const __be32 *rev;
0195 
0196     rev = of_get_property(root, "board-revision-int", &propsize);
0197     if (propsize != 4)
0198         return 0;
0199 
0200     return be32_to_cpu(*rev);
0201 }
0202 
0203 static bool __init platform_detect(void)
0204 {
0205     struct device_node *root = of_find_node_by_path("/");
0206     bool success;
0207 
0208     if (!root)
0209         return false;
0210 
0211     success = check_ofw_architecture(root);
0212     if (success) {
0213         olpc_platform_info.boardrev = get_board_revision(root);
0214         olpc_platform_info.flags |= OLPC_F_PRESENT;
0215 
0216         pr_info("OLPC board revision %s%X\n",
0217             ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
0218             olpc_platform_info.boardrev >> 4);
0219     }
0220 
0221     of_node_put(root);
0222     return success;
0223 }
0224 
0225 static int __init add_xo1_platform_devices(void)
0226 {
0227     struct platform_device *pdev;
0228 
0229     pdev = platform_device_register_simple("xo1-rfkill", -1, NULL, 0);
0230     if (IS_ERR(pdev))
0231         return PTR_ERR(pdev);
0232 
0233     pdev = platform_device_register_simple("olpc-xo1", -1, NULL, 0);
0234 
0235     return PTR_ERR_OR_ZERO(pdev);
0236 }
0237 
0238 static int olpc_xo1_ec_suspend(struct platform_device *pdev)
0239 {
0240     /*
0241      * Squelch SCIs while suspended.  This is a fix for
0242      * <http://dev.laptop.org/ticket/1835>.
0243      */
0244     return olpc_ec_cmd(EC_SET_SCI_INHIBIT, NULL, 0, NULL, 0);
0245 }
0246 
0247 static int olpc_xo1_ec_resume(struct platform_device *pdev)
0248 {
0249     /* Tell the EC to stop inhibiting SCIs */
0250     olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0);
0251 
0252     /*
0253      * Tell the wireless module to restart USB communication.
0254      * Must be done twice.
0255      */
0256     olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
0257     olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0);
0258 
0259     return 0;
0260 }
0261 
0262 static struct olpc_ec_driver ec_xo1_driver = {
0263     .suspend = olpc_xo1_ec_suspend,
0264     .resume = olpc_xo1_ec_resume,
0265     .ec_cmd = olpc_xo1_ec_cmd,
0266 #ifdef CONFIG_OLPC_XO1_SCI
0267     /*
0268      * XO-1 EC wakeups are available when olpc-xo1-sci driver is
0269      * compiled in
0270      */
0271     .wakeup_available = true,
0272 #endif
0273 };
0274 
0275 static struct olpc_ec_driver ec_xo1_5_driver = {
0276     .ec_cmd = olpc_xo1_ec_cmd,
0277 #ifdef CONFIG_OLPC_XO15_SCI
0278     /*
0279      * XO-1.5 EC wakeups are available when olpc-xo15-sci driver is
0280      * compiled in
0281      */
0282     .wakeup_available = true,
0283 #endif
0284 };
0285 
0286 static int __init olpc_init(void)
0287 {
0288     int r = 0;
0289 
0290     if (!olpc_ofw_present() || !platform_detect())
0291         return 0;
0292 
0293     /* register the XO-1 and 1.5-specific EC handler */
0294     if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) /* XO-1 */
0295         olpc_ec_driver_register(&ec_xo1_driver, NULL);
0296     else
0297         olpc_ec_driver_register(&ec_xo1_5_driver, NULL);
0298     platform_device_register_simple("olpc-ec", -1, NULL, 0);
0299 
0300     /* assume B1 and above models always have a DCON */
0301     if (olpc_board_at_least(olpc_board(0xb1)))
0302         olpc_platform_info.flags |= OLPC_F_DCON;
0303 
0304 #ifdef CONFIG_PCI_OLPC
0305     /* If the VSA exists let it emulate PCI, if not emulate in kernel.
0306      * XO-1 only. */
0307     if (olpc_platform_info.boardrev < olpc_board_pre(0xd0) &&
0308             !cs5535_has_vsa2())
0309         x86_init.pci.arch_init = pci_olpc_init;
0310 #endif
0311 
0312     if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) { /* XO-1 */
0313         r = add_xo1_platform_devices();
0314         if (r)
0315             return r;
0316     }
0317 
0318     return 0;
0319 }
0320 
0321 postcore_initcall(olpc_init);