diff options
Diffstat (limited to 'drivers')
84 files changed, 3693 insertions, 1775 deletions
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index adc877dfef5c..38fc5f397fde 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -348,7 +348,6 @@ struct rbd_client_id { struct rbd_mapping { u64 size; u64 features; - bool read_only; }; /* @@ -450,12 +449,11 @@ static DEFINE_IDA(rbd_dev_id_ida); static struct workqueue_struct *rbd_wq; /* - * Default to false for now, as single-major requires >= 0.75 version of - * userspace rbd utility. + * single-major requires >= 0.75 version of userspace rbd utility. */ -static bool single_major = false; +static bool single_major = true; module_param(single_major, bool, S_IRUGO); -MODULE_PARM_DESC(single_major, "Use a single major number for all rbd devices (default: false)"); +MODULE_PARM_DESC(single_major, "Use a single major number for all rbd devices (default: true)"); static int rbd_img_request_submit(struct rbd_img_request *img_request); @@ -608,9 +606,6 @@ static int rbd_open(struct block_device *bdev, fmode_t mode) struct rbd_device *rbd_dev = bdev->bd_disk->private_data; bool removing = false; - if ((mode & FMODE_WRITE) && rbd_dev->mapping.read_only) - return -EROFS; - spin_lock_irq(&rbd_dev->lock); if (test_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags)) removing = true; @@ -640,46 +635,24 @@ static void rbd_release(struct gendisk *disk, fmode_t mode) static int rbd_ioctl_set_ro(struct rbd_device *rbd_dev, unsigned long arg) { - int ret = 0; - int val; - bool ro; - bool ro_changed = false; + int ro; - /* get_user() may sleep, so call it before taking rbd_dev->lock */ - if (get_user(val, (int __user *)(arg))) + if (get_user(ro, (int __user *)arg)) return -EFAULT; - ro = val ? true : false; - /* Snapshot doesn't allow to write*/ + /* Snapshots can't be marked read-write */ if (rbd_dev->spec->snap_id != CEPH_NOSNAP && !ro) return -EROFS; - spin_lock_irq(&rbd_dev->lock); - /* prevent others open this device */ - if (rbd_dev->open_count > 1) { - ret = -EBUSY; - goto out; - } - - if (rbd_dev->mapping.read_only != ro) { - rbd_dev->mapping.read_only = ro; - ro_changed = true; - } - -out: - spin_unlock_irq(&rbd_dev->lock); - /* set_disk_ro() may sleep, so call it after releasing rbd_dev->lock */ - if (ret == 0 && ro_changed) - set_disk_ro(rbd_dev->disk, ro ? 1 : 0); - - return ret; + /* Let blkdev_roset() handle it */ + return -ENOTTY; } static int rbd_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { struct rbd_device *rbd_dev = bdev->bd_disk->private_data; - int ret = 0; + int ret; switch (cmd) { case BLKROSET: @@ -4050,15 +4023,8 @@ static void rbd_queue_workfn(struct work_struct *work) goto err_rq; } - /* Only reads are allowed to a read-only device */ - - if (op_type != OBJ_OP_READ) { - if (rbd_dev->mapping.read_only) { - result = -EROFS; - goto err_rq; - } - rbd_assert(rbd_dev->spec->snap_id == CEPH_NOSNAP); - } + rbd_assert(op_type == OBJ_OP_READ || + rbd_dev->spec->snap_id == CEPH_NOSNAP); /* * Quit early if the mapped snapshot no longer exists. It's @@ -4423,7 +4389,6 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) /* enable the discard support */ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q); q->limits.discard_granularity = segment_size; - q->limits.discard_alignment = segment_size; blk_queue_max_discard_sectors(q, segment_size / SECTOR_SIZE); blk_queue_max_write_zeroes_sectors(q, segment_size / SECTOR_SIZE); @@ -5994,7 +5959,7 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev) goto err_out_disk; set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE); - set_disk_ro(rbd_dev->disk, rbd_dev->mapping.read_only); + set_disk_ro(rbd_dev->disk, rbd_dev->opts->read_only); ret = dev_set_name(&rbd_dev->dev, "%d", rbd_dev->dev_id); if (ret) @@ -6145,7 +6110,6 @@ static ssize_t do_rbd_add(struct bus_type *bus, struct rbd_options *rbd_opts = NULL; struct rbd_spec *spec = NULL; struct rbd_client *rbdc; - bool read_only; int rc; if (!try_module_get(THIS_MODULE)) @@ -6194,11 +6158,8 @@ static ssize_t do_rbd_add(struct bus_type *bus, } /* If we are mapping a snapshot it must be marked read-only */ - - read_only = rbd_dev->opts->read_only; if (rbd_dev->spec->snap_id != CEPH_NOSNAP) - read_only = true; - rbd_dev->mapping.read_only = read_only; + rbd_dev->opts->read_only = true; rc = rbd_dev_device_setup(rbd_dev); if (rc) diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c index f5f2b62471da..859ddab9448f 100644 --- a/drivers/ide/ide-pnp.c +++ b/drivers/ide/ide-pnp.c @@ -22,7 +22,7 @@ #define DRV_NAME "ide-pnp" /* Add your devices here :)) */ -static struct pnp_device_id idepnp_devices[] = { +static const struct pnp_device_id idepnp_devices[] = { /* Generic ESDI/IDE/ATA compatible hard disk controller */ {.id = "PNP0600", .driver_data = 0}, {.id = ""} diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 2d46ec84ebdf..2d0897b7d860 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -3142,13 +3142,17 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp) if (!mlxsw_sp->ports) return -ENOMEM; - mlxsw_sp->port_to_module = kcalloc(max_ports, sizeof(u8), GFP_KERNEL); + mlxsw_sp->port_to_module = kmalloc_array(max_ports, sizeof(int), + GFP_KERNEL); if (!mlxsw_sp->port_to_module) { err = -ENOMEM; goto err_port_to_module_alloc; } for (i = 1; i < max_ports; i++) { + /* Mark as invalid */ + mlxsw_sp->port_to_module[i] = -1; + err = mlxsw_sp_port_module_info_get(mlxsw_sp, i, &module, &width, &lane); if (err) @@ -3216,6 +3220,8 @@ static void mlxsw_sp_port_unsplit_create(struct mlxsw_sp *mlxsw_sp, for (i = 0; i < count; i++) { local_port = base_port + i * 2; + if (mlxsw_sp->port_to_module[local_port] < 0) + continue; module = mlxsw_sp->port_to_module[local_port]; mlxsw_sp_port_create(mlxsw_sp, local_port, false, module, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 58cf222fb985..432ab9b12b7f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -152,7 +152,7 @@ struct mlxsw_sp { const struct mlxsw_bus_info *bus_info; unsigned char base_mac[ETH_ALEN]; struct mlxsw_sp_upper *lags; - u8 *port_to_module; + int *port_to_module; struct mlxsw_sp_sb *sb; struct mlxsw_sp_bridge *bridge; struct mlxsw_sp_router *router; diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index f5d73b83dcc2..553f94f55dce 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -315,6 +315,7 @@ err_free_flow: * @app: Pointer to the APP handle * @netdev: netdev structure. * @flow: TC flower classifier offload structure. + * @egress: NFP netdev is the egress. * * Adds a new flow to the repeated hash structure and action payload. * diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 2cb3622c4acc..fc0d5fa65ad4 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -2030,21 +2030,6 @@ out: return ret; } -static int rtl8169_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) -{ - struct rtl8169_private *tp = netdev_priv(dev); - int ret; - - del_timer_sync(&tp->timer); - - rtl_lock_work(tp); - ret = rtl8169_set_speed(dev, cmd->autoneg, ethtool_cmd_speed(cmd), - cmd->duplex, cmd->advertising); - rtl_unlock_work(tp); - - return ret; -} - static netdev_features_t rtl8169_fix_features(struct net_device *dev, netdev_features_t features) { @@ -2171,6 +2156,27 @@ static int rtl8169_get_link_ksettings(struct net_device *dev, return rc; } +static int rtl8169_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) +{ + struct rtl8169_private *tp = netdev_priv(dev); + int rc; + u32 advertising; + + if (!ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising)) + return -EINVAL; + + del_timer_sync(&tp->timer); + + rtl_lock_work(tp); + rc = rtl8169_set_speed(dev, cmd->base.autoneg, cmd->base.speed, + cmd->base.duplex, advertising); + rtl_unlock_work(tp); + + return rc; +} + static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) { @@ -2591,7 +2597,6 @@ static const struct ethtool_ops rtl8169_ethtool_ops = { .get_link = ethtool_op_get_link, .get_coalesce = rtl_get_coalesce, .set_coalesce = rtl_set_coalesce, - .set_settings = rtl8169_set_settings, .get_msglevel = rtl8169_get_msglevel, .set_msglevel = rtl8169_set_msglevel, .get_regs = rtl8169_get_regs, @@ -2603,6 +2608,7 @@ static const struct ethtool_ops rtl8169_ethtool_ops = { .get_ts_info = ethtool_op_get_ts_info, .nway_reset = rtl8169_nway_reset, .get_link_ksettings = rtl8169_get_link_ksettings, + .set_link_ksettings = rtl8169_set_link_ksettings, }; static void rtl8169_get_mac_version(struct rtl8169_private *tp, diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c index 72f4228a63bb..9442db221834 100644 --- a/drivers/net/phy/cortina.c +++ b/drivers/net/phy/cortina.c @@ -116,3 +116,7 @@ static struct mdio_device_id __maybe_unused cortina_tbl[] = { }; MODULE_DEVICE_TABLE(mdio, cortina_tbl); + +MODULE_DESCRIPTION("Cortina EDC CDR 10G Ethernet PHY driver"); +MODULE_AUTHOR("NXP"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/tap.c b/drivers/net/tap.c index b13890953ebb..e9489b88407c 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -1077,7 +1077,7 @@ static long tap_ioctl(struct file *file, unsigned int cmd, case TUNSETOFFLOAD: /* let the user check for future flags */ if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | - TUN_F_TSO_ECN)) + TUN_F_TSO_ECN | TUN_F_UFO)) return -EINVAL; rtnl_lock(); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 5a2ea78a008f..6a7bde9bc4b2 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2370,6 +2370,8 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) features |= NETIF_F_TSO6; arg &= ~(TUN_F_TSO4|TUN_F_TSO6); } + + arg &= ~TUN_F_UFO; } /* This gives the user a way to test for new features in future by diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 720a3a248070..c750cf7c042b 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1239,6 +1239,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1e0e, 0x9001, 5)}, /* SIMCom 7230E */ {QMI_QUIRK_SET_DTR(0x2c7c, 0x0125, 4)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */ {QMI_QUIRK_SET_DTR(0x2c7c, 0x0121, 4)}, /* Quectel EC21 Mini PCIe */ + {QMI_FIXED_INTF(0x2c7c, 0x0296, 4)}, /* Quectel BG96 */ /* 4. Gobi 1000 devices */ {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 7c3600643c7f..10b075a46b26 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -3108,6 +3108,7 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info) { struct hwsim_new_radio_params param = { 0 }; const char *hwname = NULL; + int ret; param.reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG]; param.p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE]; @@ -3147,7 +3148,9 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info) param.regd = hwsim_world_regdom_custom[idx]; } - return mac80211_hwsim_new_radio(info, ¶m); + ret = mac80211_hwsim_new_radio(info, ¶m); + kfree(hwname); + return ret; } static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info) diff --git a/drivers/ntb/hw/Kconfig b/drivers/ntb/hw/Kconfig index a89243c9fdd3..e51b581fd102 100644 --- a/drivers/ntb/hw/Kconfig +++ b/drivers/ntb/hw/Kconfig @@ -1,3 +1,4 @@ source "drivers/ntb/hw/amd/Kconfig" source "drivers/ntb/hw/idt/Kconfig" source "drivers/ntb/hw/intel/Kconfig" +source "drivers/ntb/hw/mscc/Kconfig" diff --git a/drivers/ntb/hw/Makefile b/drivers/ntb/hw/Makefile index 87332c3905f0..923c442db750 100644 --- a/drivers/ntb/hw/Makefile +++ b/drivers/ntb/hw/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_NTB_AMD) += amd/ obj-$(CONFIG_NTB_IDT) += idt/ obj-$(CONFIG_NTB_INTEL) += intel/ +obj-$(CONFIG_NTB_SWITCHTEC) += mscc/ diff --git a/drivers/ntb/hw/idt/ntb_hw_idt.c b/drivers/ntb/hw/idt/ntb_hw_idt.c index d44d7ef38fe8..0cd79f367f7c 100644 --- a/drivers/ntb/hw/idt/ntb_hw_idt.c +++ b/drivers/ntb/hw/idt/ntb_hw_idt.c @@ -2628,35 +2628,35 @@ static void idt_pci_remove(struct pci_dev *pdev) /* * IDT PCIe-switch models ports configuration structures */ -static struct idt_89hpes_cfg idt_89hpes24nt6ag2_config = { +static const struct idt_89hpes_cfg idt_89hpes24nt6ag2_config = { .name = "89HPES24NT6AG2", .port_cnt = 6, .ports = {0, 2, 4, 6, 8, 12} }; -static struct idt_89hpes_cfg idt_89hpes32nt8ag2_config = { +static const struct idt_89hpes_cfg idt_89hpes32nt8ag2_config = { .name = "89HPES32NT8AG2", .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} }; -static struct idt_89hpes_cfg idt_89hpes32nt8bg2_config = { +static const struct idt_89hpes_cfg idt_89hpes32nt8bg2_config = { .name = "89HPES32NT8BG2", .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} }; -static struct idt_89hpes_cfg idt_89hpes12nt12g2_config = { +static const struct idt_89hpes_cfg idt_89hpes12nt12g2_config = { .name = "89HPES12NT12G2", .port_cnt = 3, .ports = {0, 8, 16} }; -static struct idt_89hpes_cfg idt_89hpes16nt16g2_config = { +static const struct idt_89hpes_cfg idt_89hpes16nt16g2_config = { .name = "89HPES16NT16G2", .port_cnt = 4, .ports = {0, 8, 12, 16} }; -static struct idt_89hpes_cfg idt_89hpes24nt24g2_config = { +static const struct idt_89hpes_cfg idt_89hpes24nt24g2_config = { .name = "89HPES24NT24G2", .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} }; -static struct idt_89hpes_cfg idt_89hpes32nt24ag2_config = { +static const struct idt_89hpes_cfg idt_89hpes32nt24ag2_config = { .name = "89HPES32NT24AG2", .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} }; -static struct idt_89hpes_cfg idt_89hpes32nt24bg2_config = { +static const struct idt_89hpes_cfg idt_89hpes32nt24bg2_config = { .name = "89HPES32NT24BG2", .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20} }; diff --git a/drivers/ntb/hw/intel/ntb_hw_intel.c b/drivers/ntb/hw/intel/ntb_hw_intel.c index 2557e2c05b90..4de074a86073 100644 --- a/drivers/ntb/hw/intel/ntb_hw_intel.c +++ b/drivers/ntb/hw/intel/ntb_hw_intel.c @@ -1742,89 +1742,18 @@ static int skx_setup_b2b_mw(struct intel_ntb_dev *ndev, { struct pci_dev *pdev; void __iomem *mmio; - resource_size_t bar_size; phys_addr_t bar_addr; - int b2b_bar; - u8 bar_sz; pdev = ndev->ntb.pdev; mmio = ndev->self_mmio; - if (ndev->b2b_idx == UINT_MAX) { - dev_dbg(&pdev->dev, "not using b2b mw\n"); - b2b_bar = 0; - ndev->b2b_off = 0; - } else { - b2b_bar = ndev_mw_to_bar(ndev, ndev->b2b_idx); - if (b2b_bar < 0) - return -EIO; - - dev_dbg(&pdev->dev, "using b2b mw bar %d\n", b2b_bar); - - bar_size = pci_resource_len(ndev->ntb.pdev, b2b_bar); - - dev_dbg(&pdev->dev, "b2b bar size %#llx\n", bar_size); - - if (b2b_mw_share && ((bar_size >> 1) >= XEON_B2B_MIN_SIZE)) { - dev_dbg(&pdev->dev, "b2b using first half of bar\n"); - ndev->b2b_off = bar_size >> 1; - } else if (bar_size >= XEON_B2B_MIN_SIZE) { - dev_dbg(&pdev->dev, "b2b using whole bar\n"); - ndev->b2b_off = 0; - --ndev->mw_count; - } else { - dev_dbg(&pdev->dev, "b2b bar size is too small\n"); - return -EIO; - } - } - - /* - * Reset the secondary bar sizes to match the primary bar sizes, - * except disable or halve the size of the b2b secondary bar. - */ - pci_read_config_byte(pdev, SKX_IMBAR1SZ_OFFSET, &bar_sz); - dev_dbg(&pdev->dev, "IMBAR1SZ %#x\n", bar_sz); - if (b2b_bar == 1) { - if (ndev->b2b_off) - bar_sz -= 1; - else - bar_sz = 0; - } - - pci_write_config_byte(pdev, SKX_EMBAR1SZ_OFFSET, bar_sz); - pci_read_config_byte(pdev, SKX_EMBAR1SZ_OFFSET, &bar_sz); - dev_dbg(&pdev->dev, "EMBAR1SZ %#x\n", bar_sz); - - pci_read_config_byte(pdev, SKX_IMBAR2SZ_OFFSET, &bar_sz); - dev_dbg(&pdev->dev, "IMBAR2SZ %#x\n", bar_sz); - if (b2b_bar == 2) { - if (ndev->b2b_off) - bar_sz -= 1; - else - bar_sz = 0; - } - - pci_write_config_byte(pdev, SKX_EMBAR2SZ_OFFSET, bar_sz); - pci_read_config_byte(pdev, SKX_EMBAR2SZ_OFFSET, &bar_sz); - dev_dbg(&pdev->dev, "EMBAR2SZ %#x\n", bar_sz); - - /* SBAR01 hit by first part of the b2b bar */ - if (b2b_bar == 0) - bar_addr = addr->bar0_addr; - else if (b2b_bar == 1) - bar_addr = addr->bar2_addr64; - else if (b2b_bar == 2) - bar_addr = addr->bar4_addr64; - else - return -EIO; - /* setup incoming bar limits == base addrs (zero length windows) */ - bar_addr = addr->bar2_addr64 + (b2b_bar == 1 ? ndev->b2b_off : 0); + bar_addr = addr->bar2_addr64; iowrite64(bar_addr, mmio + SKX_IMBAR1XLMT_OFFSET); bar_addr = ioread64(mmio + SKX_IMBAR1XLMT_OFFSET); dev_dbg(&pdev->dev, "IMBAR1XLMT %#018llx\n", bar_addr); - bar_addr = addr->bar4_addr64 + (b2b_bar == 2 ? ndev->b2b_off : 0); + bar_addr = addr->bar4_addr64; iowrite64(bar_addr, mmio + SKX_IMBAR2XLMT_OFFSET); bar_addr = ioread64(mmio + SKX_IMBAR2XLMT_OFFSET); dev_dbg(&pdev->dev, "IMBAR2XLMT %#018llx\n", bar_addr); diff --git a/drivers/ntb/hw/mscc/Kconfig b/drivers/ntb/hw/mscc/Kconfig new file mode 100644 index 000000000000..013ed6716438 --- /dev/null +++ b/drivers/ntb/hw/mscc/Kconfig @@ -0,0 +1,9 @@ +config NTB_SWITCHTEC + tristate "MicroSemi Switchtec Non-Transparent Bridge Support" + select PCI_SW_SWITCHTEC + help + Enables NTB support for Switchtec PCI switches. This also + selects the Switchtec management driver as they share the same + hardware interface. + + If unsure, say N. diff --git a/drivers/ntb/hw/mscc/Makefile b/drivers/ntb/hw/mscc/Makefile new file mode 100644 index 000000000000..064686ead1ba --- /dev/null +++ b/drivers/ntb/hw/mscc/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_NTB_SWITCHTEC) += ntb_hw_switchtec.o diff --git a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c new file mode 100644 index 000000000000..afe8ed6f3b23 --- /dev/null +++ b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c @@ -0,0 +1,1216 @@ +/* + * Microsemi Switchtec(tm) PCIe Management Driver + * Copyright (c) 2017, Microsemi Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include <linux/switchtec.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/interrupt.h> +#include <linux/ntb.h> + +MODULE_DESCRIPTION("Microsemi Switchtec(tm) NTB Driver"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Microsemi Corporation"); + +static ulong max_mw_size = SZ_2M; +module_param(max_mw_size, ulong, 0644); +MODULE_PARM_DESC(max_mw_size, + "Max memory window size reported to the upper layer"); + +static bool use_lut_mws; +module_param(use_lut_mws, bool, 0644); +MODULE_PARM_DESC(use_lut_mws, + "Enable the use of the LUT based memory windows"); + +#ifndef ioread64 +#ifdef readq +#define ioread64 readq +#else +#define ioread64 _ioread64 +static inline u64 _ioread64(void __iomem *mmio) +{ + u64 low, high; + + low = ioread32(mmio); + high = ioread32(mmio + sizeof(u32)); + return low | (high << 32); +} +#endif +#endif + +#ifndef iowrite64 +#ifdef writeq +#define iowrite64 writeq +#else +#define iowrite64 _iowrite64 +static inline void _iowrite64(u64 val, void __iomem *mmio) +{ + iowrite32(val, mmio); + iowrite32(val >> 32, mmio + sizeof(u32)); +} +#endif +#endif + +#define SWITCHTEC_NTB_MAGIC 0x45CC0001 +#define MAX_MWS 128 + +struct shared_mw { + u32 magic; + u32 link_sta; + u32 partition_id; + u64 mw_sizes[MAX_MWS]; + u32 spad[128]; +}; + +#define MAX_DIRECT_MW ARRAY_SIZE(((struct ntb_ctrl_regs *)(0))->bar_entry) +#define LUT_SIZE SZ_64K + +struct switchtec_ntb { + struct ntb_dev ntb; + struct switchtec_dev *stdev; + + int self_partition; + int peer_partition; + + int doorbell_irq; + int message_irq; + + struct ntb_info_regs __iomem *mmio_ntb; + struct ntb_ctrl_regs __iomem *mmio_ctrl; + struct ntb_dbmsg_regs __iomem *mmio_dbmsg; + struct ntb_ctrl_regs __iomem *mmio_self_ctrl; + struct ntb_ctrl_regs __iomem *mmio_peer_ctrl; + struct ntb_dbmsg_regs __iomem *mmio_self_dbmsg; + + struct shared_mw *self_shared; + struct shared_mw __iomem *peer_shared; + dma_addr_t self_shared_dma; + + u64 db_mask; + u64 db_valid_mask; + int db_shift; + int db_peer_shift; + + /* synchronize rmw access of db_mask and hw reg */ + spinlock_t db_mask_lock; + + int nr_direct_mw; + int nr_lut_mw; + int direct_mw_to_bar[MAX_DIRECT_MW]; + + int peer_nr_direct_mw; + int peer_nr_lut_mw; + int peer_direct_mw_to_bar[MAX_DIRECT_MW]; + + bool link_is_up; + enum ntb_speed link_speed; + enum ntb_width link_width; +}; + +static struct switchtec_ntb *ntb_sndev(struct ntb_dev *ntb) +{ + return container_of(ntb, struct switchtec_ntb, ntb); +} + +static int switchtec_ntb_part_op(struct switchtec_ntb *sndev, + struct ntb_ctrl_regs __iomem *ctl, + u32 op, int wait_status) +{ + static const char * const op_text[] = { + [NTB_CTRL_PART_OP_LOCK] = "lock", + [NTB_CTRL_PART_OP_CFG] = "configure", + [NTB_CTRL_PART_OP_RESET] = "reset", + }; + + int i; + u32 ps; + int status; + + switch (op) { + case NTB_CTRL_PART_OP_LOCK: + status = NTB_CTRL_PART_STATUS_LOCKING; + break; + case NTB_CTRL_PART_OP_CFG: + status = NTB_CTRL_PART_STATUS_CONFIGURING; + break; + case NTB_CTRL_PART_OP_RESET: + status = NTB_CTRL_PART_STATUS_RESETTING; + break; + default: + return -EINVAL; + } + + iowrite32(op, &ctl->partition_op); + + for (i = 0; i < 1000; i++) { + if (msleep_interruptible(50) != 0) { + iowrite32(NTB_CTRL_PART_OP_RESET, &ctl->partition_op); + return -EINTR; + } + + ps = ioread32(&ctl->partition_status) & 0xFFFF; + + if (ps != status) + break; + } + + if (ps == wait_status) + return 0; + + if (ps == status) { + dev_err(&sndev->stdev->dev, + "Timed out while peforming %s (%d). (%08x)", + op_text[op], op, + ioread32(&ctl->partition_status)); + + return -ETIMEDOUT; + } + + return -EIO; +} + +static int switchtec_ntb_send_msg(struct switchtec_ntb *sndev, int idx, + u32 val) +{ + if (idx < 0 || idx >= ARRAY_SIZE(sndev->mmio_self_dbmsg->omsg)) + return -EINVAL; + + iowrite32(val, &sndev->mmio_self_dbmsg->omsg[idx].msg); + + return 0; +} + +static int switchtec_ntb_mw_count(struct ntb_dev *ntb, int pidx) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + int nr_direct_mw = sndev->peer_nr_direct_mw; + int nr_lut_mw = sndev->peer_nr_lut_mw - 1; + + if (pidx != NTB_DEF_PEER_IDX) + return -EINVAL; + + if (!use_lut_mws) + nr_lut_mw = 0; + + return nr_direct_mw + nr_lut_mw; +} + +static int lut_index(struct switchtec_ntb *sndev, int mw_idx) +{ + return mw_idx - sndev->nr_direct_mw + 1; +} + +static int peer_lut_index(struct switchtec_ntb *sndev, int mw_idx) +{ + return mw_idx - sndev->peer_nr_direct_mw + 1; +} + +static int switchtec_ntb_mw_get_align(struct ntb_dev *ntb, int pidx, + int widx, resource_size_t *addr_align, + resource_size_t *size_align, + resource_size_t *size_max) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + int lut; + resource_size_t size; + + if (pidx != NTB_DEF_PEER_IDX) + return -EINVAL; + + lut = widx >= sndev->peer_nr_direct_mw; + size = ioread64(&sndev->peer_shared->mw_sizes[widx]); + + if (size == 0) + return -EINVAL; + + if (addr_align) + *addr_align = lut ? size : SZ_4K; + + if (size_align) + *size_align = lut ? size : SZ_4K; + + if (size_max) + *size_max = size; + + return 0; +} + +static void switchtec_ntb_mw_clr_direct(struct switchtec_ntb *sndev, int idx) +{ + struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl; + int bar = sndev->peer_direct_mw_to_bar[idx]; + u32 ctl_val; + + ctl_val = ioread32(&ctl->bar_entry[bar].ctl); + ctl_val &= ~NTB_CTRL_BAR_DIR_WIN_EN; + iowrite32(ctl_val, &ctl->bar_entry[bar].ctl); + iowrite32(0, &ctl->bar_entry[bar].win_size); + iowrite64(sndev->self_partition, &ctl->bar_entry[bar].xlate_addr); +} + +static void switchtec_ntb_mw_clr_lut(struct switchtec_ntb *sndev, int idx) +{ + struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl; + + iowrite64(0, &ctl->lut_entry[peer_lut_index(sndev, idx)]); +} + +static void switchtec_ntb_mw_set_direct(struct switchtec_ntb *sndev, int idx, + dma_addr_t addr, resource_size_t size) +{ + int xlate_pos = ilog2(size); + int bar = sndev->peer_direct_mw_to_bar[idx]; + struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl; + u32 ctl_val; + + ctl_val = ioread32(&ctl->bar_entry[bar].ctl); + ctl_val |= NTB_CTRL_BAR_DIR_WIN_EN; + + iowrite32(ctl_val, &ctl->bar_entry[bar].ctl); + iowrite32(xlate_pos | size, &ctl->bar_entry[bar].win_size); + iowrite64(sndev->self_partition | addr, + &ctl->bar_entry[bar].xlate_addr); +} + +static void switchtec_ntb_mw_set_lut(struct switchtec_ntb *sndev, int idx, + dma_addr_t addr, resource_size_t size) +{ + struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl; + + iowrite64((NTB_CTRL_LUT_EN | (sndev->self_partition << 1) | addr), + &ctl->lut_entry[peer_lut_index(sndev, idx)]); +} + +static int switchtec_ntb_mw_set_trans(struct ntb_dev *ntb, int pidx, int widx, + dma_addr_t addr, resource_size_t size) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl; + int xlate_pos = ilog2(size); + int nr_direct_mw = sndev->peer_nr_direct_mw; + int rc; + + if (pidx != NTB_DEF_PEER_IDX) + return -EINVAL; + + dev_dbg(&sndev->stdev->dev, "MW %d: part %d addr %pad size %pap", + widx, pidx, &addr, &size); + + if (widx >= switchtec_ntb_mw_count(ntb, pidx)) + return -EINVAL; + + if (xlate_pos < 12) + return -EINVAL; + + rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK, + NTB_CTRL_PART_STATUS_LOCKED); + if (rc) + return rc; + + if (addr == 0 || size == 0) { + if (widx < nr_direct_mw) + switchtec_ntb_mw_clr_direct(sndev, widx); + else + switchtec_ntb_mw_clr_lut(sndev, widx); + } else { + if (widx < nr_direct_mw) + switchtec_ntb_mw_set_direct(sndev, widx, addr, size); + else + switchtec_ntb_mw_set_lut(sndev, widx, addr, size); + } + + rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG, + NTB_CTRL_PART_STATUS_NORMAL); + + if (rc == -EIO) { + dev_err(&sndev->stdev->dev, + "Hardware reported an error configuring mw %d: %08x", + widx, ioread32(&ctl->bar_error)); + + if (widx < nr_direct_mw) + switchtec_ntb_mw_clr_direct(sndev, widx); + else + switchtec_ntb_mw_clr_lut(sndev, widx); + + switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG, + NTB_CTRL_PART_STATUS_NORMAL); + } + + return rc; +} + +static int switchtec_ntb_peer_mw_count(struct ntb_dev *ntb) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + return sndev->nr_direct_mw + (use_lut_mws ? sndev->nr_lut_mw - 1 : 0); +} + +static int switchtec_ntb_direct_get_addr(struct switchtec_ntb *sndev, + int idx, phys_addr_t *base, + resource_size_t *size) +{ + int bar = sndev->direct_mw_to_bar[idx]; + size_t offset = 0; + + if (bar < 0) + return -EINVAL; + + if (idx == 0) { + /* + * This is the direct BAR shared with the LUTs + * which means the actual window will be offset + * by the size of all the LUT entries. + */ + + offset = LUT_SIZE * sndev->nr_lut_mw; + } + + if (base) + *base = pci_resource_start(sndev->ntb.pdev, bar) + offset; + + if (size) { + *size = pci_resource_len(sndev->ntb.pdev, bar) - offset; + if (offset && *size > offset) + *size = offset; + + if (*size > max_mw_size) + *size = max_mw_size; + } + + return 0; +} + +static int switchtec_ntb_lut_get_addr(struct switchtec_ntb *sndev, + int idx, phys_addr_t *base, + resource_size_t *size) +{ + int bar = sndev->direct_mw_to_bar[0]; + int offset; + + offset = LUT_SIZE * lut_index(sndev, idx); + + if (base) + *base = pci_resource_start(sndev->ntb.pdev, bar) + offset; + + if (size) + *size = LUT_SIZE; + + return 0; +} + +static int switchtec_ntb_peer_mw_get_addr(struct ntb_dev *ntb, int idx, + phys_addr_t *base, + resource_size_t *size) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (idx < sndev->nr_direct_mw) + return switchtec_ntb_direct_get_addr(sndev, idx, base, size); + else if (idx < switchtec_ntb_peer_mw_count(ntb)) + return switchtec_ntb_lut_get_addr(sndev, idx, base, size); + else + return -EINVAL; +} + +static void switchtec_ntb_part_link_speed(struct switchtec_ntb *sndev, + int partition, + enum ntb_speed *speed, + enum ntb_width *width) +{ + struct switchtec_dev *stdev = sndev->stdev; + + u32 pff = ioread32(&stdev->mmio_part_cfg[partition].vep_pff_inst_id); + u32 linksta = ioread32(&stdev->mmio_pff_csr[pff].pci_cap_region[13]); + + if (speed) + *speed = (linksta >> 16) & 0xF; + + if (width) + *width = (linksta >> 20) & 0x3F; +} + +static void switchtec_ntb_set_link_speed(struct switchtec_ntb *sndev) +{ + enum ntb_speed self_speed, peer_speed; + enum ntb_width self_width, peer_width; + + if (!sndev->link_is_up) { + sndev->link_speed = NTB_SPEED_NONE; + sndev->link_width = NTB_WIDTH_NONE; + return; + } + + switchtec_ntb_part_link_speed(sndev, sndev->self_partition, + &self_speed, &self_width); + switchtec_ntb_part_link_speed(sndev, sndev->peer_partition, + &peer_speed, &peer_width); + + sndev->link_speed = min(self_speed, peer_speed); + sndev->link_width = min(self_width, peer_width); +} + +enum { + LINK_MESSAGE = 0, + MSG_LINK_UP = 1, + MSG_LINK_DOWN = 2, + MSG_CHECK_LINK = 3, +}; + +static void switchtec_ntb_check_link(struct switchtec_ntb *sndev) +{ + int link_sta; + int old = sndev->link_is_up; + + link_sta = sndev->self_shared->link_sta; + if (link_sta) { + u64 peer = ioread64(&sndev->peer_shared->magic); + + if ((peer & 0xFFFFFFFF) == SWITCHTEC_NTB_MAGIC) + link_sta = peer >> 32; + else + link_sta = 0; + } + + sndev->link_is_up = link_sta; + switchtec_ntb_set_link_speed(sndev); + + if (link_sta != old) { + switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_CHECK_LINK); + ntb_link_event(&sndev->ntb); + dev_info(&sndev->stdev->dev, "ntb link %s", + link_sta ? "up" : "down"); + } +} + +static void switchtec_ntb_link_notification(struct switchtec_dev *stdev) +{ + struct switchtec_ntb *sndev = stdev->sndev; + + switchtec_ntb_check_link(sndev); +} + +static u64 switchtec_ntb_link_is_up(struct ntb_dev *ntb, + enum ntb_speed *speed, + enum ntb_width *width) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (speed) + *speed = sndev->link_speed; + if (width) + *width = sndev->link_width; + + return sndev->link_is_up; +} + +static int switchtec_ntb_link_enable(struct ntb_dev *ntb, + enum ntb_speed max_speed, + enum ntb_width max_width) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + dev_dbg(&sndev->stdev->dev, "enabling link"); + + sndev->self_shared->link_sta = 1; + switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_LINK_UP); + + switchtec_ntb_check_link(sndev); + + return 0; +} + +static int switchtec_ntb_link_disable(struct ntb_dev *ntb) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + dev_dbg(&sndev->stdev->dev, "disabling link"); + + sndev->self_shared->link_sta = 0; + switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_LINK_UP); + + switchtec_ntb_check_link(sndev); + + return 0; +} + +static u64 switchtec_ntb_db_valid_mask(struct ntb_dev *ntb) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + return sndev->db_valid_mask; +} + +static int switchtec_ntb_db_vector_count(struct ntb_dev *ntb) +{ + return 1; +} + +static u64 switchtec_ntb_db_vector_mask(struct ntb_dev *ntb, int db_vector) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (db_vector < 0 || db_vector > 1) + return 0; + + return sndev->db_valid_mask; +} + +static u64 switchtec_ntb_db_read(struct ntb_dev *ntb) +{ + u64 ret; + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + ret = ioread64(&sndev->mmio_self_dbmsg->idb) >> sndev->db_shift; + + return ret & sndev->db_valid_mask; +} + +static int switchtec_ntb_db_clear(struct ntb_dev *ntb, u64 db_bits) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + iowrite64(db_bits << sndev->db_shift, &sndev->mmio_self_dbmsg->idb); + + return 0; +} + +static int switchtec_ntb_db_set_mask(struct ntb_dev *ntb, u64 db_bits) +{ + unsigned long irqflags; + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (db_bits & ~sndev->db_valid_mask) + return -EINVAL; + + spin_lock_irqsave(&sndev->db_mask_lock, irqflags); + + sndev->db_mask |= db_bits << sndev->db_shift; + iowrite64(~sndev->db_mask, &sndev->mmio_self_dbmsg->idb_mask); + + spin_unlock_irqrestore(&sndev->db_mask_lock, irqflags); + + return 0; +} + +static int switchtec_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) +{ + unsigned long irqflags; + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (db_bits & ~sndev->db_valid_mask) + return -EINVAL; + + spin_lock_irqsave(&sndev->db_mask_lock, irqflags); + + sndev->db_mask &= ~(db_bits << sndev->db_shift); + iowrite64(~sndev->db_mask, &sndev->mmio_self_dbmsg->idb_mask); + + spin_unlock_irqrestore(&sndev->db_mask_lock, irqflags); + + return 0; +} + +static u64 switchtec_ntb_db_read_mask(struct ntb_dev *ntb) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + return (sndev->db_mask >> sndev->db_shift) & sndev->db_valid_mask; +} + +static int switchtec_ntb_peer_db_addr(struct ntb_dev *ntb, + phys_addr_t *db_addr, + resource_size_t *db_size) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + unsigned long offset; + + offset = (unsigned long)sndev->mmio_self_dbmsg->odb - + (unsigned long)sndev->stdev->mmio; + + offset += sndev->db_shift / 8; + + if (db_addr) + *db_addr = pci_resource_start(ntb->pdev, 0) + offset; + if (db_size) + *db_size = sizeof(u32); + + return 0; +} + +static int switchtec_ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + iowrite64(db_bits << sndev->db_peer_shift, + &sndev->mmio_self_dbmsg->odb); + + return 0; +} + +static int switchtec_ntb_spad_count(struct ntb_dev *ntb) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + return ARRAY_SIZE(sndev->self_shared->spad); +} + +static u32 switchtec_ntb_spad_read(struct ntb_dev *ntb, int idx) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (idx < 0 || idx >= ARRAY_SIZE(sndev->self_shared->spad)) + return 0; + + if (!sndev->self_shared) + return 0; + + return sndev->self_shared->spad[idx]; +} + +static int switchtec_ntb_spad_write(struct ntb_dev *ntb, int idx, u32 val) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (idx < 0 || idx >= ARRAY_SIZE(sndev->self_shared->spad)) + return -EINVAL; + + if (!sndev->self_shared) + return -EIO; + + sndev->self_shared->spad[idx] = val; + + return 0; +} + +static u32 switchtec_ntb_peer_spad_read(struct ntb_dev *ntb, int pidx, + int sidx) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (pidx != NTB_DEF_PEER_IDX) + return -EINVAL; + + if (sidx < 0 || sidx >= ARRAY_SIZE(sndev->peer_shared->spad)) + return 0; + + if (!sndev->peer_shared) + return 0; + + return ioread32(&sndev->peer_shared->spad[sidx]); +} + +static int switchtec_ntb_peer_spad_write(struct ntb_dev *ntb, int pidx, + int sidx, u32 val) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + + if (pidx != NTB_DEF_PEER_IDX) + return -EINVAL; + + if (sidx < 0 || sidx >= ARRAY_SIZE(sndev->peer_shared->spad)) + return -EINVAL; + + if (!sndev->peer_shared) + return -EIO; + + iowrite32(val, &sndev->peer_shared->spad[sidx]); + + return 0; +} + +static int switchtec_ntb_peer_spad_addr(struct ntb_dev *ntb, int pidx, + int sidx, phys_addr_t *spad_addr) +{ + struct switchtec_ntb *sndev = ntb_sndev(ntb); + unsigned long offset; + + if (pidx != NTB_DEF_PEER_IDX) + return -EINVAL; + + offset = (unsigned long)&sndev->peer_shared->spad[sidx] - + (unsigned long)sndev->stdev->mmio; + + if (spad_addr) + *spad_addr = pci_resource_start(ntb->pdev, 0) + offset; + + return 0; +} + +static const struct ntb_dev_ops switchtec_ntb_ops = { + .mw_count = switchtec_ntb_mw_count, + .mw_get_align = switchtec_ntb_mw_get_align, + .mw_set_trans = switchtec_ntb_mw_set_trans, + .peer_mw_count = switchtec_ntb_peer_mw_count, + .peer_mw_get_addr = switchtec_ntb_peer_mw_get_addr, + .link_is_up = switchtec_ntb_link_is_up, + .link_enable = switchtec_ntb_link_enable, + .link_disable = switchtec_ntb_link_disable, + .db_valid_mask = switchtec_ntb_db_valid_mask, + .db_vector_count = switchtec_ntb_db_vector_count, + .db_vector_mask = switchtec_ntb_db_vector_mask, + .db_read = switchtec_ntb_db_read, + .db_clear = switchtec_ntb_db_clear, + .db_set_mask = switchtec_ntb_db_set_mask, + .db_clear_mask = switchtec_ntb_db_clear_mask, + .db_read_mask = switchtec_ntb_db_read_mask, + .peer_db_addr = switchtec_ntb_peer_db_addr, + .peer_db_set = switchtec_ntb_peer_db_set, + .spad_count = switchtec_ntb_spad_count, + .spad_read = switchtec_ntb_spad_read, + .spad_write = switchtec_ntb_spad_write, + .peer_spad_read = switchtec_ntb_peer_spad_read, + .peer_spad_write = switchtec_ntb_peer_spad_write, + .peer_spad_addr = switchtec_ntb_peer_spad_addr, +}; + +static void switchtec_ntb_init_sndev(struct switchtec_ntb *sndev) +{ + u64 part_map; + + sndev->ntb.pdev = sndev->stdev->pdev; + sndev->ntb.topo = NTB_TOPO_SWITCH; + sndev->ntb.ops = &switchtec_ntb_ops; + + sndev->self_partition = sndev->stdev->partition; + + sndev->mmio_ntb = sndev->stdev->mmio_ntb; + part_map = ioread64(&sndev->mmio_ntb->ep_map); + part_map &= ~(1 << sndev->self_partition); + sndev->peer_partition = ffs(part_map) - 1; + + dev_dbg(&sndev->stdev->dev, "Partition ID %d of %d (%llx)", + sndev->self_partition, sndev->stdev->partition_count, + part_map); + + sndev->mmio_ctrl = (void * __iomem)sndev->mmio_ntb + + SWITCHTEC_NTB_REG_CTRL_OFFSET; + sndev->mmio_dbmsg = (void * __iomem)sndev->mmio_ntb + + SWITCHTEC_NTB_REG_DBMSG_OFFSET; + + sndev->mmio_self_ctrl = &sndev->mmio_ctrl[sndev->self_partition]; + sndev->mmio_peer_ctrl = &sndev->mmio_ctrl[sndev->peer_partition]; + sndev->mmio_self_dbmsg = &sndev->mmio_dbmsg[sndev->self_partition]; +} + +static int map_bars(int *map, struct ntb_ctrl_regs __iomem *ctrl) +{ + int i; + int cnt = 0; + + for (i = 0; i < ARRAY_SIZE(ctrl->bar_entry); i++) { + u32 r = ioread32(&ctrl->bar_entry[i].ctl); + + if (r & NTB_CTRL_BAR_VALID) + map[cnt++] = i; + } + + return cnt; +} + +static void switchtec_ntb_init_mw(struct switchtec_ntb *sndev) +{ + sndev->nr_direct_mw = map_bars(sndev->direct_mw_to_bar, + sndev->mmio_self_ctrl); + + sndev->nr_lut_mw = ioread16(&sndev->mmio_self_ctrl->lut_table_entries); + sndev->nr_lut_mw = rounddown_pow_of_two(sndev->nr_lut_mw); + + dev_dbg(&sndev->stdev->dev, "MWs: %d direct, %d lut", + sndev->nr_direct_mw, sndev->nr_lut_mw); + + sndev->peer_nr_direct_mw = map_bars(sndev->peer_direct_mw_to_bar, + sndev->mmio_peer_ctrl); + + sndev->peer_nr_lut_mw = + ioread16(&sndev->mmio_peer_ctrl->lut_table_entries); + sndev->peer_nr_lut_mw = rounddown_pow_of_two(sndev->peer_nr_lut_mw); + + dev_dbg(&sndev->stdev->dev, "Peer MWs: %d direct, %d lut", + sndev->peer_nr_direct_mw, sndev->peer_nr_lut_mw); + +} + +/* + * There are 64 doorbells in the switch hardware but this is + * shared among all partitions. So we must split them in half + * (32 for each partition). However, the message interrupts are + * also shared with the top 4 doorbells so we just limit this to + * 28 doorbells per partition + */ +static void switchtec_ntb_init_db(struct switchtec_ntb *sndev) +{ + sndev->db_valid_mask = 0x0FFFFFFF; + + if (sndev->self_partition < sndev->peer_partition) { + sndev->db_shift = 0; + sndev->db_peer_shift = 32; + } else { + sndev->db_shift = 32; + sndev->db_peer_shift = 0; + } + + sndev->db_mask = 0x0FFFFFFFFFFFFFFFULL; + iowrite64(~sndev->db_mask, &sndev->mmio_self_dbmsg->idb_mask); + iowrite64(sndev->db_valid_mask << sndev->db_peer_shift, + &sndev->mmio_self_dbmsg->odb_mask); +} + +static void switchtec_ntb_init_msgs(struct switchtec_ntb *sndev) +{ + int i; + u32 msg_map = 0; + + for (i = 0; i < ARRAY_SIZE(sndev->mmio_self_dbmsg->imsg); i++) { + int m = i | sndev->peer_partition << 2; + + msg_map |= m << i * 8; + } + + iowrite32(msg_map, &sndev->mmio_self_dbmsg->msg_map); + + for (i = 0; i < ARRAY_SIZE(sndev->mmio_self_dbmsg->imsg); i++) + iowrite64(NTB_DBMSG_IMSG_STATUS | NTB_DBMSG_IMSG_MASK, + &sndev->mmio_self_dbmsg->imsg[i]); +} + +static int switchtec_ntb_init_req_id_table(struct switchtec_ntb *sndev) +{ + int rc = 0; + u16 req_id; + u32 error; + + req_id = ioread16(&sndev->mmio_ntb->requester_id); + + if (ioread32(&sndev->mmio_self_ctrl->req_id_table_size) < 2) { + dev_err(&sndev->stdev->dev, + "Not enough requester IDs available."); + return -EFAULT; + } + + rc = switchtec_ntb_part_op(sndev, sndev->mmio_self_ctrl, + NTB_CTRL_PART_OP_LOCK, + NTB_CTRL_PART_STATUS_LOCKED); + if (rc) + return rc; + + iowrite32(NTB_PART_CTRL_ID_PROT_DIS, + &sndev->mmio_self_ctrl->partition_ctrl); + + /* + * Root Complex Requester ID (which is 0:00.0) + */ + iowrite32(0 << 16 | NTB_CTRL_REQ_ID_EN, + &sndev->mmio_self_ctrl->req_id_table[0]); + + /* + * Host Bridge Requester ID (as read from the mmap address) + */ + iowrite32(req_id << 16 | NTB_CTRL_REQ_ID_EN, + &sndev->mmio_self_ctrl->req_id_table[1]); + + rc = switchtec_ntb_part_op(sndev, sndev->mmio_self_ctrl, + NTB_CTRL_PART_OP_CFG, + NTB_CTRL_PART_STATUS_NORMAL); + if (rc == -EIO) { + error = ioread32(&sndev->mmio_self_ctrl->req_id_error); + dev_err(&sndev->stdev->dev, + "Error setting up the requester ID table: %08x", + error); + } + + return rc; +} + +static void switchtec_ntb_init_shared(struct switchtec_ntb *sndev) +{ + int i; + + memset(sndev->self_shared, 0, LUT_SIZE); + sndev->self_shared->magic = SWITCHTEC_NTB_MAGIC; + sndev->self_shared->partition_id = sndev->stdev->partition; + + for (i = 0; i < sndev->nr_direct_mw; i++) { + int bar = sndev->direct_mw_to_bar[i]; + resource_size_t sz = pci_resource_len(sndev->stdev->pdev, bar); + + if (i == 0) + sz = min_t(resource_size_t, sz, + LUT_SIZE * sndev->nr_lut_mw); + + sndev->self_shared->mw_sizes[i] = sz; + } + + for (i = 0; i < sndev->nr_lut_mw; i++) { + int idx = sndev->nr_direct_mw + i; + + sndev->self_shared->mw_sizes[idx] = LUT_SIZE; + } +} + +static int switchtec_ntb_init_shared_mw(struct switchtec_ntb *sndev) +{ + struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl; + int bar = sndev->direct_mw_to_bar[0]; + u32 ctl_val; + int rc; + + sndev->self_shared = dma_zalloc_coherent(&sndev->stdev->pdev->dev, + LUT_SIZE, + &sndev->self_shared_dma, + GFP_KERNEL); + if (!sndev->self_shared) { + dev_err(&sndev->stdev->dev, + "unable to allocate memory for shared mw"); + return -ENOMEM; + } + + switchtec_ntb_init_shared(sndev); + + rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK, + NTB_CTRL_PART_STATUS_LOCKED); + if (rc) + goto unalloc_and_exit; + + ctl_val = ioread32(&ctl->bar_entry[bar].ctl); + ctl_val &= 0xFF; + ctl_val |= NTB_CTRL_BAR_LUT_WIN_EN; + ctl_val |= ilog2(LUT_SIZE) << 8; + ctl_val |= (sndev->nr_lut_mw - 1) << 14; + iowrite32(ctl_val, &ctl->bar_entry[bar].ctl); + + iowrite64((NTB_CTRL_LUT_EN | (sndev->self_partition << 1) | + sndev->self_shared_dma), + &ctl->lut_entry[0]); + + rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG, + NTB_CTRL_PART_STATUS_NORMAL); + if (rc) { + u32 bar_error, lut_error; + + bar_error = ioread32(&ctl->bar_error); + lut_error = ioread32(&ctl->lut_error); + dev_err(&sndev->stdev->dev, + "Error setting up shared MW: %08x / %08x", + bar_error, lut_error); + goto unalloc_and_exit; + } + + sndev->peer_shared = pci_iomap(sndev->stdev->pdev, bar, LUT_SIZE); + if (!sndev->peer_shared) { + rc = -ENOMEM; + goto unalloc_and_exit; + } + + dev_dbg(&sndev->stdev->dev, "Shared MW Ready"); + return 0; + +unalloc_and_exit: + dma_free_coherent(&sndev->stdev->pdev->dev, LUT_SIZE, + sndev->self_shared, sndev->self_shared_dma); + + return rc; +} + +static void switchtec_ntb_deinit_shared_mw(struct switchtec_ntb *sndev) +{ + if (sndev->peer_shared) + pci_iounmap(sndev->stdev->pdev, sndev->peer_shared); + + if (sndev->self_shared) + dma_free_coherent(&sndev->stdev->pdev->dev, LUT_SIZE, + sndev->self_shared, + sndev->self_shared_dma); +} + +static irqreturn_t switchtec_ntb_doorbell_isr(int irq, void *dev) +{ + struct switchtec_ntb *sndev = dev; + + dev_dbg(&sndev->stdev->dev, "doorbell\n"); + + ntb_db_event(&sndev->ntb, 0); + + return IRQ_HANDLED; +} + +static irqreturn_t switchtec_ntb_message_isr(int irq, void *dev) +{ + int i; + struct switchtec_ntb *sndev = dev; + + for (i = 0; i < ARRAY_SIZE(sndev->mmio_self_dbmsg->imsg); i++) { + u64 msg = ioread64(&sndev->mmio_self_dbmsg->imsg[i]); + + if (msg & NTB_DBMSG_IMSG_STATUS) { + dev_dbg(&sndev->stdev->dev, "message: %d %08x\n", i, + (u32)msg); + iowrite8(1, &sndev->mmio_self_dbmsg->imsg[i].status); + + if (i == LINK_MESSAGE) + switchtec_ntb_check_link(sndev); + } + } + + return IRQ_HANDLED; +} + +static int switchtec_ntb_init_db_msg_irq(struct switchtec_ntb *sndev) +{ + int i; + int rc; + int doorbell_irq = 0; + int message_irq = 0; + int event_irq; + int idb_vecs = sizeof(sndev->mmio_self_dbmsg->idb_vec_map); + + event_irq = ioread32(&sndev->stdev->mmio_part_cfg->vep_vector_number); + + while (doorbell_irq == event_irq) + doorbell_irq++; + while (message_irq == doorbell_irq || + message_irq == event_irq) + message_irq++; + + dev_dbg(&sndev->stdev->dev, "irqs - event: %d, db: %d, msgs: %d", + event_irq, doorbell_irq, message_irq); + + for (i = 0; i < idb_vecs - 4; i++) + iowrite8(doorbell_irq, + &sndev->mmio_self_dbmsg->idb_vec_map[i]); + + for (; i < idb_vecs; i++) + iowrite8(message_irq, + &sndev->mmio_self_dbmsg->idb_vec_map[i]); + + sndev->doorbell_irq = pci_irq_vector(sndev->stdev->pdev, doorbell_irq); + sndev->message_irq = pci_irq_vector(sndev->stdev->pdev, message_irq); + + rc = request_irq(sndev->doorbell_irq, + switchtec_ntb_doorbell_isr, 0, + "switchtec_ntb_doorbell", sndev); + if (rc) + return rc; + + rc = request_irq(sndev->message_irq, + switchtec_ntb_message_isr, 0, + "switchtec_ntb_message", sndev); + if (rc) { + free_irq(sndev->doorbell_irq, sndev); + return rc; + } + + return 0; +} + +static void switchtec_ntb_deinit_db_msg_irq(struct switchtec_ntb *sndev) +{ + free_irq(sndev->doorbell_irq, sndev); + free_irq(sndev->message_irq, sndev); +} + +static int switchtec_ntb_add(struct device *dev, + struct class_interface *class_intf) +{ + struct switchtec_dev *stdev = to_stdev(dev); + struct switchtec_ntb *sndev; + int rc; + + stdev->sndev = NULL; + + if (stdev->pdev->class != MICROSEMI_NTB_CLASSCODE) + return -ENODEV; + + if (stdev->partition_count != 2) + dev_warn(dev, "ntb driver only supports 2 partitions"); + + sndev = kzalloc_node(sizeof(*sndev), GFP_KERNEL, dev_to_node(dev)); + if (!sndev) + return -ENOMEM; + + sndev->stdev = stdev; + switchtec_ntb_init_sndev(sndev); + switchtec_ntb_init_mw(sndev); + switchtec_ntb_init_db(sndev); + switchtec_ntb_init_msgs(sndev); + + rc = switchtec_ntb_init_req_id_table(sndev); + if (rc) + goto free_and_exit; + + rc = switchtec_ntb_init_shared_mw(sndev); + if (rc) + goto free_and_exit; + + rc = switchtec_ntb_init_db_msg_irq(sndev); + if (rc) + goto deinit_shared_and_exit; + + rc = ntb_register_device(&sndev->ntb); + if (rc) + goto deinit_and_exit; + + stdev->sndev = sndev; + stdev->link_notifier = switchtec_ntb_link_notification; + dev_info(dev, "NTB device registered"); + + return 0; + +deinit_and_exit: + switchtec_ntb_deinit_db_msg_irq(sndev); +deinit_shared_and_exit: + switchtec_ntb_deinit_shared_mw(sndev); +free_and_exit: + kfree(sndev); + dev_err(dev, "failed to register ntb device: %d", rc); + return rc; +} + +void switchtec_ntb_remove(struct device *dev, + struct class_interface *class_intf) +{ + struct switchtec_dev *stdev = to_stdev(dev); + struct switchtec_ntb *sndev = stdev->sndev; + + if (!sndev) + return; + + stdev->link_notifier = NULL; + stdev->sndev = NULL; + ntb_unregister_device(&sndev->ntb); + switchtec_ntb_deinit_db_msg_irq(sndev); + switchtec_ntb_deinit_shared_mw(sndev); + kfree(sndev); + dev_info(dev, "ntb device unregistered"); +} + +static struct class_interface switchtec_interface = { + .add_dev = switchtec_ntb_add, + .remove_dev = switchtec_ntb_remove, +}; + +static int __init switchtec_ntb_init(void) +{ + switchtec_interface.class = switchtec_class; + return class_interface_register(&switchtec_interface); +} +module_init(switchtec_ntb_init); + +static void __exit switchtec_ntb_exit(void) +{ + class_interface_unregister(&switchtec_interface); +} +module_exit(switchtec_ntb_exit); diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index f58d8e305323..045e3dd4750e 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -191,8 +191,6 @@ struct ntb_transport_qp { struct ntb_transport_mw { phys_addr_t phys_addr; resource_size_t phys_size; - resource_size_t xlat_align; - resource_size_t xlat_align_size; void __iomem *vbase; size_t xlat_size; size_t buff_size; @@ -687,13 +685,20 @@ static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, struct ntb_transport_mw *mw = &nt->mw_vec[num_mw]; struct pci_dev *pdev = nt->ndev->pdev; size_t xlat_size, buff_size; + resource_size_t xlat_align; + resource_size_t xlat_align_size; int rc; if (!size) return -EINVAL; - xlat_size = round_up(size, mw->xlat_align_size); - buff_size = round_up(size, mw->xlat_align); + rc = ntb_mw_get_align(nt->ndev, PIDX, num_mw, &xlat_align, + &xlat_align_size, NULL); + if (rc) + return rc; + + xlat_size = round_up(size, xlat_align_size); + buff_size = round_up(size, xlat_align); /* No need to re-setup */ if (mw->xlat_size == xlat_size) @@ -722,7 +727,7 @@ static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, * is a requirement of the hardware. It is recommended to setup CMA * for BAR sizes equal or greater than 4MB. */ - if (!IS_ALIGNED(mw->dma_addr, mw->xlat_align)) { + if (!IS_ALIGNED(mw->dma_addr, xlat_align)) { dev_err(&pdev->dev, "DMA memory %pad is not aligned\n", &mw->dma_addr); ntb_free_mw(nt, num_mw); @@ -1104,11 +1109,6 @@ static int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev) for (i = 0; i < mw_count; i++) { mw = &nt->mw_vec[i]; - rc = ntb_mw_get_align(ndev, PIDX, i, &mw->xlat_align, - &mw->xlat_align_size, NULL); - if (rc) - goto err1; - rc = ntb_peer_mw_get_addr(ndev, i, &mw->phys_addr, &mw->phys_size); if (rc) diff --git a/drivers/ntb/test/ntb_perf.c b/drivers/ntb/test/ntb_perf.c index 759f772fa00c..427112cf101a 100644 --- a/drivers/ntb/test/ntb_perf.c +++ b/drivers/ntb/test/ntb_perf.c @@ -108,8 +108,6 @@ MODULE_PARM_DESC(on_node, "Run threads only on NTB device node (default: true)") struct perf_mw { phys_addr_t phys_addr; resource_size_t phys_size; - resource_size_t xlat_align; - resource_size_t xlat_align_size; void __iomem *vbase; size_t xlat_size; size_t buf_size; @@ -472,13 +470,20 @@ static int perf_set_mw(struct perf_ctx *perf, resource_size_t size) { struct perf_mw *mw = &perf->mw; size_t xlat_size, buf_size; + resource_size_t xlat_align; + resource_size_t xlat_align_size; int rc; if (!size) return -EINVAL; - xlat_size = round_up(size, mw->xlat_align_size); - buf_size = round_up(size, mw->xlat_align); + rc = ntb_mw_get_align(perf->ntb, PIDX, 0, &xlat_align, + &xlat_align_size, NULL); + if (rc) + return rc; + + xlat_size = round_up(size, xlat_align_size); + buf_size = round_up(size, xlat_align); if (mw->xlat_size == xlat_size) return 0; @@ -567,11 +572,6 @@ static int perf_setup_mw(struct ntb_dev *ntb, struct perf_ctx *perf) mw = &perf->mw; - rc = ntb_mw_get_align(ntb, PIDX, 0, &mw->xlat_align, - &mw->xlat_align_size, NULL); - if (rc) - return rc; - rc = ntb_peer_mw_get_addr(ntb, 0, &mw->phys_addr, &mw->phys_size); if (rc) return rc; diff --git a/drivers/ntb/test/ntb_tool.c b/drivers/ntb/test/ntb_tool.c index a69815c45ce6..91526a986caa 100644 --- a/drivers/ntb/test/ntb_tool.c +++ b/drivers/ntb/test/ntb_tool.c @@ -753,9 +753,9 @@ static ssize_t tool_peer_mw_trans_read(struct file *filep, phys_addr_t base; resource_size_t mw_size; - resource_size_t align_addr; - resource_size_t align_size; - resource_size_t max_size; + resource_size_t align_addr = 0; + resource_size_t align_size = 0; + resource_size_t max_size = 0; buf_size = min_t(size_t, size, 512); diff --git a/drivers/of/base.c b/drivers/of/base.c index f2e649ff746f..26618ba8f92a 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -761,10 +761,10 @@ EXPORT_SYMBOL(of_find_node_opts_by_path); /** * of_find_node_by_name - Find a node by its "name" property - * @from: The node to start searching from or NULL, the node + * @from: The node to start searching from or NULL; the node * you pass will not be searched, only the next one - * will; typically, you pass what the previous call - * returned. of_node_put() will be called on it + * will. Typically, you pass what the previous call + * returned. of_node_put() will be called on @from. * @name: The name string to match against * * Returns a node pointer with refcount incremented, use diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index e9ec931f5b9a..a7b1cb6c2f65 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -374,7 +374,7 @@ int of_pci_map_rid(struct device_node *np, u32 rid, pr_debug("%pOF: %s, using mask %08x, rid-base: %08x, out-base: %08x, length: %08x, rid: %08x -> %08x\n", np, map_name, map_mask, rid_base, out_base, - rid_len, rid, *id_out); + rid_len, rid, masked_rid - rid_base + out_base); return 0; } diff --git a/drivers/of/unittest-data/Makefile b/drivers/of/unittest-data/Makefile index 3031fc2f18f6..32389acfa616 100644 --- a/drivers/of/unittest-data/Makefile +++ b/drivers/of/unittest-data/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +DTC_FLAGS_testcases := -Wno-interrupts_property obj-y += testcases.dtb.o targets += testcases.dtb testcases.dtb.S diff --git a/drivers/of/unittest-data/testcases.dts b/drivers/of/unittest-data/testcases.dts index ce49463d9d32..55fe0ee20109 100644 --- a/drivers/of/unittest-data/testcases.dts +++ b/drivers/of/unittest-data/testcases.dts @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /dts-v1/; +/plugin/; + / { testcase-data { changeset { @@ -15,66 +17,3 @@ #include "tests-match.dtsi" #include "tests-platform.dtsi" #include "tests-overlay.dtsi" - -/* - * phandle fixup data - generated by dtc patches that aren't upstream. - * This data must be regenerated whenever phandle references are modified in - * the testdata tree. - * - * The format of this data may be subject to change. For the time being consider - * this a kernel-internal data format. - */ -/ { __local_fixups__ { - testcase-data { - phandle-tests { - consumer-a { - phandle-list = <0x00000000 0x00000008 - 0x00000018 0x00000028 - 0x00000034 0x00000038>; - phandle-list-bad-args = <0x00000000 0x0000000c>; - }; - }; - interrupts { - intmap0 { - interrupt-map = <0x00000004 0x00000010 - 0x00000024 0x00000034>; - }; - intmap1 { - interrupt-map = <0x0000000c>; - }; - interrupts0 { - interrupt-parent = <0x00000000>; - }; - interrupts1 { - interrupt-parent = <0x00000000>; - }; - interrupts-extended0 { - interrupts-extended = <0x00000000 0x00000008 - 0x00000018 0x00000024 - 0x0000002c 0x00000034 - 0x0000003c>; - }; - }; - testcase-device1 { - interrupt-parent = <0x00000000>; - }; - testcase-device2 { - interrupt-parent = <0x00000000>; - }; - overlay2 { - fragment@0 { - target = <0x00000000>; - }; - }; - overlay3 { - fragment@0 { - target = <0x00000000>; - }; - }; - overlay4 { - fragment@0 { - target = <0x00000000>; - }; - }; - }; -}; }; diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index da45dbea20ce..730cc897b94d 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -13,6 +13,7 @@ * */ +#include <linux/switchtec.h> #include <linux/switchtec_ioctl.h> #include <linux/interrupt.h> @@ -20,8 +21,6 @@ #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/poll.h> -#include <linux/pci.h> -#include <linux/cdev.h> #include <linux/wait.h> MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver"); @@ -34,265 +33,10 @@ module_param(max_devices, int, 0644); MODULE_PARM_DESC(max_devices, "max number of switchtec device instances"); static dev_t switchtec_devt; -static struct class *switchtec_class; static DEFINE_IDA(switchtec_minor_ida); -#define MICROSEMI_VENDOR_ID 0x11f8 -#define MICROSEMI_NTB_CLASSCODE 0x068000 -#define MICROSEMI_MGMT_CLASSCODE 0x058000 - -#define SWITCHTEC_MRPC_PAYLOAD_SIZE 1024 -#define SWITCHTEC_MAX_PFF_CSR 48 - -#define SWITCHTEC_EVENT_OCCURRED BIT(0) -#define SWITCHTEC_EVENT_CLEAR BIT(0) -#define SWITCHTEC_EVENT_EN_LOG BIT(1) -#define SWITCHTEC_EVENT_EN_CLI BIT(2) -#define SWITCHTEC_EVENT_EN_IRQ BIT(3) -#define SWITCHTEC_EVENT_FATAL BIT(4) - -enum { - SWITCHTEC_GAS_MRPC_OFFSET = 0x0000, - SWITCHTEC_GAS_TOP_CFG_OFFSET = 0x1000, - SWITCHTEC_GAS_SW_EVENT_OFFSET = 0x1800, - SWITCHTEC_GAS_SYS_INFO_OFFSET = 0x2000, - SWITCHTEC_GAS_FLASH_INFO_OFFSET = 0x2200, - SWITCHTEC_GAS_PART_CFG_OFFSET = 0x4000, - SWITCHTEC_GAS_NTB_OFFSET = 0x10000, - SWITCHTEC_GAS_PFF_CSR_OFFSET = 0x134000, -}; - -struct mrpc_regs { - u8 input_data[SWITCHTEC_MRPC_PAYLOAD_SIZE]; - u8 output_data[SWITCHTEC_MRPC_PAYLOAD_SIZE]; - u32 cmd; - u32 status; - u32 ret_value; -} __packed; - -enum mrpc_status { - SWITCHTEC_MRPC_STATUS_INPROGRESS = 1, - SWITCHTEC_MRPC_STATUS_DONE = 2, - SWITCHTEC_MRPC_STATUS_ERROR = 0xFF, - SWITCHTEC_MRPC_STATUS_INTERRUPTED = 0x100, -}; - -struct sw_event_regs { - u64 event_report_ctrl; - u64 reserved1; - u64 part_event_bitmap; - u64 reserved2; - u32 global_summary; - u32 reserved3[3]; - u32 stack_error_event_hdr; - u32 stack_error_event_data; - u32 reserved4[4]; - u32 ppu_error_event_hdr; - u32 ppu_error_event_data; - u32 reserved5[4]; - u32 isp_error_event_hdr; - u32 isp_error_event_data; - u32 reserved6[4]; - u32 sys_reset_event_hdr; - u32 reserved7[5]; - u32 fw_exception_hdr; - u32 reserved8[5]; - u32 fw_nmi_hdr; - u32 reserved9[5]; - u32 fw_non_fatal_hdr; - u32 reserved10[5]; - u32 fw_fatal_hdr; - u32 reserved11[5]; - u32 twi_mrpc_comp_hdr; - u32 twi_mrpc_comp_data; - u32 reserved12[4]; - u32 twi_mrpc_comp_async_hdr; - u32 twi_mrpc_comp_async_data; - u32 reserved13[4]; - u32 cli_mrpc_comp_hdr; - u32 cli_mrpc_comp_data; - u32 reserved14[4]; - u32 cli_mrpc_comp_async_hdr; - u32 cli_mrpc_comp_async_data; - u32 reserved15[4]; - u32 gpio_interrupt_hdr; - u32 gpio_interrupt_data; - u32 reserved16[4]; -} __packed; - -enum { - SWITCHTEC_CFG0_RUNNING = 0x04, - SWITCHTEC_CFG1_RUNNING = 0x05, - SWITCHTEC_IMG0_RUNNING = 0x03, - SWITCHTEC_IMG1_RUNNING = 0x07, -}; - -struct sys_info_regs { - u32 device_id; - u32 device_version; - u32 firmware_version; - u32 reserved1; - u32 vendor_table_revision; - u32 table_format_version; - u32 partition_id; - u32 cfg_file_fmt_version; - u16 cfg_running; - u16 img_running; - u32 reserved2[57]; - char vendor_id[8]; - char product_id[16]; - char product_revision[4]; - char component_vendor[8]; - u16 component_id; - u8 component_revision; -} __packed; - -struct flash_info_regs { - u32 flash_part_map_upd_idx; - - struct active_partition_info { - u32 address; - u32 build_version; - u32 build_string; - } active_img; - - struct active_partition_info active_cfg; - struct active_partition_info inactive_img; - struct active_partition_info inactive_cfg; - - u32 flash_length; - - struct partition_info { - u32 address; - u32 length; - } cfg0; - - struct partition_info cfg1; - struct partition_info img0; - struct partition_info img1; - struct partition_info nvlog; - struct partition_info vendor[8]; -}; - -struct ntb_info_regs { - u8 partition_count; - u8 partition_id; - u16 reserved1; - u64 ep_map; - u16 requester_id; -} __packed; - -struct part_cfg_regs { - u32 status; - u32 state; - u32 port_cnt; - u32 usp_port_mode; - u32 usp_pff_inst_id; - u32 vep_pff_inst_id; - u32 dsp_pff_inst_id[47]; - u32 reserved1[11]; - u16 vep_vector_number; - u16 usp_vector_number; - u32 port_event_bitmap; - u32 reserved2[3]; - u32 part_event_summary; - u32 reserved3[3]; - u32 part_reset_hdr; - u32 part_reset_data[5]; - u32 mrpc_comp_hdr; - u32 mrpc_comp_data[5]; - u32 mrpc_comp_async_hdr; - u32 mrpc_comp_async_data[5]; - u32 dyn_binding_hdr; - u32 dyn_binding_data[5]; - u32 reserved4[159]; -} __packed; - -enum { - SWITCHTEC_PART_CFG_EVENT_RESET = 1 << 0, - SWITCHTEC_PART_CFG_EVENT_MRPC_CMP = 1 << 1, - SWITCHTEC_PART_CFG_EVENT_MRPC_ASYNC_CMP = 1 << 2, - SWITCHTEC_PART_CFG_EVENT_DYN_PART_CMP = 1 << 3, -}; - -struct pff_csr_regs { - u16 vendor_id; - u16 device_id; - u32 pci_cfg_header[15]; - u32 pci_cap_region[48]; - u32 pcie_cap_region[448]; - u32 indirect_gas_window[128]; - u32 indirect_gas_window_off; - u32 reserved[127]; - u32 pff_event_summary; - u32 reserved2[3]; - u32 aer_in_p2p_hdr; - u32 aer_in_p2p_data[5]; - u32 aer_in_vep_hdr; - u32 aer_in_vep_data[5]; - u32 dpc_hdr; - u32 dpc_data[5]; - u32 cts_hdr; - u32 cts_data[5]; - u32 reserved3[6]; - u32 hotplug_hdr; - u32 hotplug_data[5]; - u32 ier_hdr; - u32 ier_data[5]; - u32 threshold_hdr; - u32 threshold_data[5]; - u32 power_mgmt_hdr; - u32 power_mgmt_data[5]; - u32 tlp_throttling_hdr; - u32 tlp_throttling_data[5]; - u32 force_speed_hdr; - u32 force_speed_data[5]; - u32 credit_timeout_hdr; - u32 credit_timeout_data[5]; - u32 link_state_hdr; - u32 link_state_data[5]; - u32 reserved4[174]; -} __packed; - -struct switchtec_dev { - struct pci_dev *pdev; - struct device dev; - struct cdev cdev; - - int partition; - int partition_count; - int pff_csr_count; - char pff_local[SWITCHTEC_MAX_PFF_CSR]; - - void __iomem *mmio; - struct mrpc_regs __iomem *mmio_mrpc; - struct sw_event_regs __iomem *mmio_sw_event; - struct sys_info_regs __iomem *mmio_sys_info; - struct flash_info_regs __iomem *mmio_flash_info; - struct ntb_info_regs __iomem *mmio_ntb; - struct part_cfg_regs __iomem *mmio_part_cfg; - struct part_cfg_regs __iomem *mmio_part_cfg_all; - struct pff_csr_regs __iomem *mmio_pff_csr; - - /* - * The mrpc mutex must be held when accessing the other - * mrpc_ fields, alive flag and stuser->state field - */ - struct mutex mrpc_mutex; - struct list_head mrpc_queue; - int mrpc_busy; - struct work_struct mrpc_work; - struct delayed_work mrpc_timeout; - bool alive; - - wait_queue_head_t event_wq; - atomic_t event_cnt; -}; - -static struct switchtec_dev *to_stdev(struct device *dev) -{ - return container_of(dev, struct switchtec_dev, dev); -} +struct class *switchtec_class; +EXPORT_SYMBOL_GPL(switchtec_class); enum mrpc_state { MRPC_IDLE = 0, @@ -1234,6 +978,49 @@ static const struct file_operations switchtec_fops = { .compat_ioctl = switchtec_dev_ioctl, }; +static void link_event_work(struct work_struct *work) +{ + struct switchtec_dev *stdev; + + stdev = container_of(work, struct switchtec_dev, link_event_work); + + if (stdev->link_notifier) + stdev->link_notifier(stdev); +} + +static void check_link_state_events(struct switchtec_dev *stdev) +{ + int idx; + u32 reg; + int count; + int occurred = 0; + + for (idx = 0; idx < stdev->pff_csr_count; idx++) { + reg = ioread32(&stdev->mmio_pff_csr[idx].link_state_hdr); + dev_dbg(&stdev->dev, "link_state: %d->%08x\n", idx, reg); + count = (reg >> 5) & 0xFF; + + if (count != stdev->link_event_count[idx]) { + occurred = 1; + stdev->link_event_count[idx] = count; + } + } + + if (occurred) + schedule_work(&stdev->link_event_work); +} + +static void enable_link_state_events(struct switchtec_dev *stdev) +{ + int idx; + + for (idx = 0; idx < stdev->pff_csr_count; idx++) { + iowrite32(SWITCHTEC_EVENT_CLEAR | + SWITCHTEC_EVENT_EN_IRQ, + &stdev->mmio_pff_csr[idx].link_state_hdr); + } +} + static void stdev_release(struct device *dev) { struct switchtec_dev *stdev = to_stdev(dev); @@ -1286,6 +1073,7 @@ static struct switchtec_dev *stdev_create(struct pci_dev *pdev) stdev->mrpc_busy = 0; INIT_WORK(&stdev->mrpc_work, mrpc_event_work); INIT_DELAYED_WORK(&stdev->mrpc_timeout, mrpc_timeout_work); + INIT_WORK(&stdev->link_event_work, link_event_work); init_waitqueue_head(&stdev->event_wq); atomic_set(&stdev->event_cnt, 0); @@ -1329,6 +1117,9 @@ static int mask_event(struct switchtec_dev *stdev, int eid, int idx) if (!(hdr & SWITCHTEC_EVENT_OCCURRED && hdr & SWITCHTEC_EVENT_EN_IRQ)) return 0; + if (eid == SWITCHTEC_IOCTL_EVENT_LINK_STATE) + return 0; + dev_dbg(&stdev->dev, "%s: %d %d %x\n", __func__, eid, idx, hdr); hdr &= ~(SWITCHTEC_EVENT_EN_IRQ | SWITCHTEC_EVENT_OCCURRED); iowrite32(hdr, hdr_reg); @@ -1348,6 +1139,7 @@ static int mask_all_events(struct switchtec_dev *stdev, int eid) for (idx = 0; idx < stdev->pff_csr_count; idx++) { if (!stdev->pff_local[idx]) continue; + count += mask_event(stdev, eid, idx); } } else { @@ -1372,6 +1164,8 @@ static irqreturn_t switchtec_event_isr(int irq, void *dev) iowrite32(reg, &stdev->mmio_part_cfg->mrpc_comp_hdr); } + check_link_state_events(stdev); + for (eid = 0; eid < SWITCHTEC_IOCTL_MAX_EVENTS; eid++) event_count += mask_all_events(stdev, eid); @@ -1481,6 +1275,9 @@ static int switchtec_pci_probe(struct pci_dev *pdev, struct switchtec_dev *stdev; int rc; + if (pdev->class == MICROSEMI_NTB_CLASSCODE) + request_module_nowait("ntb_hw_switchtec"); + stdev = stdev_create(pdev); if (IS_ERR(stdev)) return PTR_ERR(stdev); @@ -1498,6 +1295,7 @@ static int switchtec_pci_probe(struct pci_dev *pdev, iowrite32(SWITCHTEC_EVENT_CLEAR | SWITCHTEC_EVENT_EN_IRQ, &stdev->mmio_part_cfg->mrpc_comp_hdr); + enable_link_state_events(stdev); rc = cdev_device_add(&stdev->cdev, &stdev->dev); if (rc) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 09dac11337d1..2c745e8ccad6 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -93,12 +93,31 @@ config ASUS_LAPTOP config DELL_SMBIOS tristate - select DCDBAS + +config DELL_SMBIOS_WMI + tristate "Dell SMBIOS calling interface (WMI implementation)" + depends on ACPI_WMI + select DELL_WMI_DESCRIPTOR + select DELL_SMBIOS + ---help--- + This provides an implementation for the Dell SMBIOS calling interface + communicated over ACPI-WMI. + + If you have a Dell computer from >2007 you should say Y or M here. + If you aren't sure and this module doesn't work for your computer + it just won't load. + +config DELL_SMBIOS_SMM + tristate "Dell SMBIOS calling interface (SMM implementation)" + depends on DCDBAS + select DELL_SMBIOS ---help--- - This module provides common functions for kernel modules using - Dell SMBIOS. + This provides an implementation for the Dell SMBIOS calling interface + communicated over SMI/SMM. - If you have a Dell laptop, say Y or M here. + If you have a Dell computer from <=2017 you should say Y or M here. + If you aren't sure and this module doesn't work for your computer + it just won't load. config DELL_LAPTOP tristate "Dell Laptop Extras" @@ -116,11 +135,12 @@ config DELL_LAPTOP laptops (except for some models covered by the Compal driver). config DELL_WMI - tristate "Dell WMI extras" + tristate "Dell WMI notifications" depends on ACPI_WMI depends on DMI depends on INPUT depends on ACPI_VIDEO || ACPI_VIDEO = n + select DELL_WMI_DESCRIPTOR select DELL_SMBIOS select INPUT_SPARSEKMAP ---help--- @@ -129,6 +149,10 @@ config DELL_WMI To compile this driver as a module, choose M here: the module will be called dell-wmi. +config DELL_WMI_DESCRIPTOR + tristate + depends on ACPI_WMI + config DELL_WMI_AIO tristate "WMI Hotkeys for Dell All-In-One series" depends on ACPI_WMI @@ -426,7 +450,6 @@ config THINKPAD_ACPI_ALSA_SUPPORT config THINKPAD_ACPI_DEBUGFACILITIES bool "Maintainer debug facilities" depends on THINKPAD_ACPI - default n ---help--- Enables extra stuff in the thinkpad-acpi which is completely useless for normal use. Read the driver source to find out what it does. @@ -437,7 +460,6 @@ config THINKPAD_ACPI_DEBUGFACILITIES config THINKPAD_ACPI_DEBUG bool "Verbose debug mode" depends on THINKPAD_ACPI - default n ---help--- Enables extra debugging information, at the expense of a slightly increase in driver size. @@ -447,7 +469,6 @@ config THINKPAD_ACPI_DEBUG config THINKPAD_ACPI_UNSAFE_LEDS bool "Allow control of important LEDs (unsafe)" depends on THINKPAD_ACPI - default n ---help--- Overriding LED state on ThinkPads can mask important firmware alerts (like critical battery condition), or misled @@ -515,7 +536,6 @@ config SENSORS_HDAPS tristate "Thinkpad Hard Drive Active Protection System (hdaps)" depends on INPUT select INPUT_POLLDEV - default n help This driver provides support for the IBM Hard Drive Active Protection System (hdaps), which provides an accelerometer and other misc. data. @@ -658,6 +678,18 @@ config WMI_BMOF To compile this driver as a module, choose M here: the module will be called wmi-bmof. +config INTEL_WMI_THUNDERBOLT + tristate "Intel WMI thunderbolt force power driver" + depends on ACPI_WMI + ---help--- + Say Y here if you want to be able to use the WMI interface on select + systems to force the power control of Intel Thunderbolt controllers. + This is useful for updating the firmware when devices are not plugged + into the controller. + + To compile this driver as a module, choose M here: the module will + be called intel-wmi-thunderbolt. + config MSI_WMI tristate "MSI WMI extras" depends on ACPI_WMI @@ -763,7 +795,6 @@ config TOSHIBA_HAPS config TOSHIBA_WMI tristate "Toshiba WMI Hotkeys Driver (EXPERIMENTAL)" - default n depends on ACPI_WMI depends on INPUT select INPUT_SPARSEKMAP @@ -785,7 +816,6 @@ config ACPI_CMPC depends on RFKILL || RFKILL=n select INPUT select BACKLIGHT_CLASS_DEVICE - default n help Support for Intel Classmate PC ACPI devices, including some keys as input device, backlight device, tablet and accelerometer @@ -793,7 +823,7 @@ config ACPI_CMPC config INTEL_CHT_INT33FE tristate "Intel Cherry Trail ACPI INT33FE Driver" - depends on X86 && ACPI && I2C + depends on X86 && ACPI && I2C && REGULATOR ---help--- This driver add support for the INT33FE ACPI device found on some Intel Cherry Trail devices. @@ -804,6 +834,10 @@ config INTEL_CHT_INT33FE This driver instantiates i2c-clients for these, so that standard i2c drivers for these chips can bind to the them. + If you enable this driver it is advised to also select + CONFIG_TYPEC_FUSB302=m, CONFIG_CHARGER_BQ24190=m and + CONFIG_BATTERY_MAX17042=m. + config INTEL_INT0002_VGPIO tristate "Intel ACPI INT0002 Virtual GPIO driver" depends on GPIOLIB && ACPI @@ -892,7 +926,6 @@ config INTEL_IPS config INTEL_IMR bool "Intel Isolated Memory Region support" - default n depends on X86_INTEL_QUARK && IOSF_MBI ---help--- This option provides a means to manipulate Isolated Memory Regions. @@ -1088,7 +1121,6 @@ config INTEL_PUNIT_IPC config INTEL_TELEMETRY tristate "Intel SoC Telemetry Driver" - default n depends on INTEL_PMC_IPC && INTEL_PUNIT_IPC && X86_64 ---help--- This driver provides interfaces to configure and use @@ -1111,7 +1143,6 @@ config MLX_PLATFORM config MLX_CPLD_PLATFORM tristate "Mellanox platform hotplug driver support" - default n select HWMON select I2C ---help--- diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index f9e3ae683bbe..c32b34a72467 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -13,8 +13,11 @@ obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o +obj-$(CONFIG_DELL_SMBIOS_WMI) += dell-smbios-wmi.o +obj-$(CONFIG_DELL_SMBIOS_SMM) += dell-smbios-smm.o obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o obj-$(CONFIG_DELL_WMI) += dell-wmi.o +obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o @@ -40,6 +43,7 @@ obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o +obj-$(CONFIG_INTEL_WMI_THUNDERBOLT) += intel-wmi-thunderbolt.o # toshiba_acpi must link after wmi to ensure that wmi devices are found # before toshiba_acpi initializes diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 48e1541dc8d4..a32c5c00e0e7 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -119,6 +119,7 @@ MODULE_LICENSE("GPL"); #define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 #define ASUS_WMI_DEVID_LIGHT_SENSOR 0x00050022 /* ?? */ +#define ASUS_WMI_DEVID_LIGHTBAR 0x00050025 /* Misc */ #define ASUS_WMI_DEVID_CAMERA 0x00060013 @@ -148,6 +149,7 @@ MODULE_LICENSE("GPL"); #define ASUS_WMI_DSTS_BIOS_BIT 0x00040000 #define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF #define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00 +#define ASUS_WMI_DSTS_LIGHTBAR_MASK 0x0000000F #define ASUS_FAN_DESC "cpu_fan" #define ASUS_FAN_MFUN 0x13 @@ -222,10 +224,13 @@ struct asus_wmi { int tpd_led_wk; struct led_classdev kbd_led; int kbd_led_wk; + struct led_classdev lightbar_led; + int lightbar_led_wk; struct workqueue_struct *led_workqueue; struct work_struct tpd_led_work; struct work_struct kbd_led_work; struct work_struct wlan_led_work; + struct work_struct lightbar_led_work; struct asus_rfkill wlan; struct asus_rfkill bluetooth; @@ -567,6 +572,48 @@ static enum led_brightness wlan_led_get(struct led_classdev *led_cdev) return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK; } +static void lightbar_led_update(struct work_struct *work) +{ + struct asus_wmi *asus; + int ctrl_param; + + asus = container_of(work, struct asus_wmi, lightbar_led_work); + + ctrl_param = asus->lightbar_led_wk; + asus_wmi_set_devstate(ASUS_WMI_DEVID_LIGHTBAR, ctrl_param, NULL); +} + +static void lightbar_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct asus_wmi *asus; + + asus = container_of(led_cdev, struct asus_wmi, lightbar_led); + + asus->lightbar_led_wk = !!value; + queue_work(asus->led_workqueue, &asus->lightbar_led_work); +} + +static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev) +{ + struct asus_wmi *asus; + u32 result; + + asus = container_of(led_cdev, struct asus_wmi, lightbar_led); + asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result); + + return result & ASUS_WMI_DSTS_LIGHTBAR_MASK; +} + +static int lightbar_led_presence(struct asus_wmi *asus) +{ + u32 result; + + asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result); + + return result & ASUS_WMI_DSTS_PRESENCE_BIT; +} + static void asus_wmi_led_exit(struct asus_wmi *asus) { if (!IS_ERR_OR_NULL(asus->kbd_led.dev)) @@ -575,6 +622,8 @@ static void asus_wmi_led_exit(struct asus_wmi *asus) led_classdev_unregister(&asus->tpd_led); if (!IS_ERR_OR_NULL(asus->wlan_led.dev)) led_classdev_unregister(&asus->wlan_led); + if (!IS_ERR_OR_NULL(asus->lightbar_led.dev)) + led_classdev_unregister(&asus->lightbar_led); if (asus->led_workqueue) destroy_workqueue(asus->led_workqueue); } @@ -630,6 +679,20 @@ static int asus_wmi_led_init(struct asus_wmi *asus) rv = led_classdev_register(&asus->platform_device->dev, &asus->wlan_led); + if (rv) + goto error; + } + + if (lightbar_led_presence(asus)) { + INIT_WORK(&asus->lightbar_led_work, lightbar_led_update); + + asus->lightbar_led.name = "asus::lightbar"; + asus->lightbar_led.brightness_set = lightbar_led_set; + asus->lightbar_led.brightness_get = lightbar_led_get; + asus->lightbar_led.max_brightness = 1; + + rv = led_classdev_register(&asus->platform_device->dev, + &asus->lightbar_led); } error: diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index f42159fd2031..2d704361f672 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -35,18 +35,6 @@ #include "dell-rbtn.h" #include "dell-smbios.h" -#define BRIGHTNESS_TOKEN 0x7d -#define KBD_LED_OFF_TOKEN 0x01E1 -#define KBD_LED_ON_TOKEN 0x01E2 -#define KBD_LED_AUTO_TOKEN 0x01E3 -#define KBD_LED_AUTO_25_TOKEN 0x02EA -#define KBD_LED_AUTO_50_TOKEN 0x02EB -#define KBD_LED_AUTO_75_TOKEN 0x02EC -#define KBD_LED_AUTO_100_TOKEN 0x02F6 -#define GLOBAL_MIC_MUTE_ENABLE 0x0364 -#define GLOBAL_MIC_MUTE_DISABLE 0x0365 -#define KBD_LED_AC_TOKEN 0x0451 - struct quirk_entry { u8 touchpad_led; @@ -85,6 +73,7 @@ static struct platform_driver platform_driver = { } }; +static struct calling_interface_buffer *buffer; static struct platform_device *platform_device; static struct backlight_device *dell_backlight_device; static struct rfkill *wifi_rfkill; @@ -283,6 +272,27 @@ static const struct dmi_system_id dell_quirks[] __initconst = { { } }; +void dell_set_arguments(u32 arg0, u32 arg1, u32 arg2, u32 arg3) +{ + memset(buffer, 0, sizeof(struct calling_interface_buffer)); + buffer->input[0] = arg0; + buffer->input[1] = arg1; + buffer->input[2] = arg2; + buffer->input[3] = arg3; +} + +int dell_send_request(u16 class, u16 select) +{ + int ret; + + buffer->cmd_class = class; + buffer->cmd_select = select; + ret = dell_smbios_call(buffer); + if (ret != 0) + return ret; + return dell_smbios_error(buffer->output[0]); +} + /* * Derived from information in smbios-wireless-ctl: * @@ -405,7 +415,6 @@ static const struct dmi_system_id dell_quirks[] __initconst = { static int dell_rfkill_set(void *data, bool blocked) { - struct calling_interface_buffer *buffer; int disable = blocked ? 1 : 0; unsigned long radio = (unsigned long)data; int hwswitch_bit = (unsigned long)data - 1; @@ -413,20 +422,16 @@ static int dell_rfkill_set(void *data, bool blocked) int status; int ret; - buffer = dell_smbios_get_buffer(); - - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0, 0, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); + if (ret) + return ret; status = buffer->output[1]; - if (ret != 0) - goto out; - - dell_smbios_clear_buffer(); - - buffer->input[0] = 0x2; - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0x2, 0, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); + if (ret) + return ret; hwswitch = buffer->output[1]; /* If the hardware switch controls this radio, and the hardware @@ -435,28 +440,19 @@ static int dell_rfkill_set(void *data, bool blocked) (status & BIT(0)) && !(status & BIT(16))) disable = 1; - dell_smbios_clear_buffer(); - - buffer->input[0] = (1 | (radio<<8) | (disable << 16)); - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; - - out: - dell_smbios_release_buffer(); - return dell_smbios_error(ret); + dell_set_arguments(1 | (radio<<8) | (disable << 16), 0, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); + return ret; } -/* Must be called with the buffer held */ static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio, - int status, - struct calling_interface_buffer *buffer) + int status) { if (status & BIT(0)) { /* Has hw-switch, sync sw_state to BIOS */ int block = rfkill_blocked(rfkill); - dell_smbios_clear_buffer(); - buffer->input[0] = (1 | (radio << 8) | (block << 16)); - dell_smbios_send_request(17, 11); + dell_set_arguments(1 | (radio << 8) | (block << 16), 0, 0, 0); + dell_send_request(CLASS_INFO, SELECT_RFKILL); } else { /* No hw-switch, sync BIOS state to sw_state */ rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16))); @@ -472,32 +468,23 @@ static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio, static void dell_rfkill_query(struct rfkill *rfkill, void *data) { - struct calling_interface_buffer *buffer; int radio = ((unsigned long)data & 0xF); int hwswitch; int status; int ret; - buffer = dell_smbios_get_buffer(); - - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0, 0, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); status = buffer->output[1]; if (ret != 0 || !(status & BIT(0))) { - dell_smbios_release_buffer(); return; } - dell_smbios_clear_buffer(); - - buffer->input[0] = 0x2; - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0, 0x2, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); hwswitch = buffer->output[1]; - dell_smbios_release_buffer(); - if (ret != 0) return; @@ -513,27 +500,23 @@ static struct dentry *dell_laptop_dir; static int dell_debugfs_show(struct seq_file *s, void *data) { - struct calling_interface_buffer *buffer; int hwswitch_state; int hwswitch_ret; int status; int ret; - buffer = dell_smbios_get_buffer(); - - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0, 0, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); + if (ret) + return ret; status = buffer->output[1]; - dell_smbios_clear_buffer(); - - buffer->input[0] = 0x2; - dell_smbios_send_request(17, 11); - hwswitch_ret = buffer->output[0]; + dell_set_arguments(0, 0x2, 0, 0); + hwswitch_ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); + if (hwswitch_ret) + return hwswitch_ret; hwswitch_state = buffer->output[1]; - dell_smbios_release_buffer(); - seq_printf(s, "return:\t%d\n", ret); seq_printf(s, "status:\t0x%X\n", status); seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n", @@ -613,46 +596,36 @@ static const struct file_operations dell_debugfs_fops = { static void dell_update_rfkill(struct work_struct *ignored) { - struct calling_interface_buffer *buffer; int hwswitch = 0; int status; int ret; - buffer = dell_smbios_get_buffer(); - - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0, 0, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); status = buffer->output[1]; if (ret != 0) - goto out; - - dell_smbios_clear_buffer(); + return; - buffer->input[0] = 0x2; - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0, 0x2, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); if (ret == 0 && (status & BIT(0))) hwswitch = buffer->output[1]; if (wifi_rfkill) { dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch); - dell_rfkill_update_sw_state(wifi_rfkill, 1, status, buffer); + dell_rfkill_update_sw_state(wifi_rfkill, 1, status); } if (bluetooth_rfkill) { dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status, hwswitch); - dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status, - buffer); + dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status); } if (wwan_rfkill) { dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch); - dell_rfkill_update_sw_state(wwan_rfkill, 3, status, buffer); + dell_rfkill_update_sw_state(wwan_rfkill, 3, status); } - - out: - dell_smbios_release_buffer(); } static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); @@ -696,7 +669,6 @@ static struct notifier_block dell_laptop_rbtn_notifier = { static int __init dell_setup_rfkill(void) { - struct calling_interface_buffer *buffer; int status, ret, whitelisted; const char *product; @@ -712,11 +684,9 @@ static int __init dell_setup_rfkill(void) if (!force_rfkill && !whitelisted) return 0; - buffer = dell_smbios_get_buffer(); - dell_smbios_send_request(17, 11); - ret = buffer->output[0]; + dell_set_arguments(0, 0, 0, 0); + ret = dell_send_request(CLASS_INFO, SELECT_RFKILL); status = buffer->output[1]; - dell_smbios_release_buffer(); /* dell wireless info smbios call is not supported */ if (ret != 0) @@ -869,7 +839,6 @@ static void dell_cleanup_rfkill(void) static int dell_send_intensity(struct backlight_device *bd) { - struct calling_interface_buffer *buffer; struct calling_interface_token *token; int ret; @@ -877,24 +846,17 @@ static int dell_send_intensity(struct backlight_device *bd) if (!token) return -ENODEV; - buffer = dell_smbios_get_buffer(); - buffer->input[0] = token->location; - buffer->input[1] = bd->props.brightness; - + dell_set_arguments(token->location, bd->props.brightness, 0, 0); if (power_supply_is_system_supplied() > 0) - dell_smbios_send_request(1, 2); + ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_AC); else - dell_smbios_send_request(1, 1); + ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT); - ret = dell_smbios_error(buffer->output[0]); - - dell_smbios_release_buffer(); return ret; } static int dell_get_intensity(struct backlight_device *bd) { - struct calling_interface_buffer *buffer; struct calling_interface_token *token; int ret; @@ -902,20 +864,14 @@ static int dell_get_intensity(struct backlight_device *bd) if (!token) return -ENODEV; - buffer = dell_smbios_get_buffer(); - buffer->input[0] = token->location; - + dell_set_arguments(token->location, 0, 0, 0); if (power_supply_is_system_supplied() > 0) - dell_smbios_send_request(0, 2); + ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_AC); else - dell_smbios_send_request(0, 1); + ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_BAT); - if (buffer->output[0]) - ret = dell_smbios_error(buffer->output[0]); - else + if (ret == 0) ret = buffer->output[1]; - - dell_smbios_release_buffer(); return ret; } @@ -1179,20 +1135,13 @@ static DEFINE_MUTEX(kbd_led_mutex); static int kbd_get_info(struct kbd_info *info) { - struct calling_interface_buffer *buffer; u8 units; int ret; - buffer = dell_smbios_get_buffer(); - - buffer->input[0] = 0x0; - dell_smbios_send_request(4, 11); - ret = buffer->output[0]; - - if (ret) { - ret = dell_smbios_error(ret); - goto out; - } + dell_set_arguments(0, 0, 0, 0); + ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT); + if (ret) + return ret; info->modes = buffer->output[1] & 0xFFFF; info->type = (buffer->output[1] >> 24) & 0xFF; @@ -1209,8 +1158,6 @@ static int kbd_get_info(struct kbd_info *info) if (units & BIT(3)) info->days = (buffer->output[3] >> 24) & 0xFF; - out: - dell_smbios_release_buffer(); return ret; } @@ -1269,19 +1216,12 @@ static int kbd_set_level(struct kbd_state *state, u8 level) static int kbd_get_state(struct kbd_state *state) { - struct calling_interface_buffer *buffer; int ret; - buffer = dell_smbios_get_buffer(); - - buffer->input[0] = 0x1; - dell_smbios_send_request(4, 11); - ret = buffer->output[0]; - - if (ret) { - ret = dell_smbios_error(ret); - goto out; - } + dell_set_arguments(0x1, 0, 0, 0); + ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT); + if (ret) + return ret; state->mode_bit = ffs(buffer->output[1] & 0xFFFF); if (state->mode_bit != 0) @@ -1296,31 +1236,27 @@ static int kbd_get_state(struct kbd_state *state) state->timeout_value_ac = (buffer->output[2] >> 24) & 0x3F; state->timeout_unit_ac = (buffer->output[2] >> 30) & 0x3; - out: - dell_smbios_release_buffer(); return ret; } static int kbd_set_state(struct kbd_state *state) { - struct calling_interface_buffer *buffer; int ret; + u32 input1; + u32 input2; + + input1 = BIT(state->mode_bit) & 0xFFFF; + input1 |= (state->triggers & 0xFF) << 16; + input1 |= (state->timeout_value & 0x3F) << 24; + input1 |= (state->timeout_unit & 0x3) << 30; + input2 = state->als_setting & 0xFF; + input2 |= (state->level & 0xFF) << 16; + input2 |= (state->timeout_value_ac & 0x3F) << 24; + input2 |= (state->timeout_unit_ac & 0x3) << 30; + dell_set_arguments(0x2, input1, input2, 0); + ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT); - buffer = dell_smbios_get_buffer(); - buffer->input[0] = 0x2; - buffer->input[1] = BIT(state->mode_bit) & 0xFFFF; - buffer->input[1] |= (state->triggers & 0xFF) << 16; - buffer->input[1] |= (state->timeout_value & 0x3F) << 24; - buffer->input[1] |= (state->timeout_unit & 0x3) << 30; - buffer->input[2] = state->als_setting & 0xFF; - buffer->input[2] |= (state->level & 0xFF) << 16; - buffer->input[2] |= (state->timeout_value_ac & 0x3F) << 24; - buffer->input[2] |= (state->timeout_unit_ac & 0x3) << 30; - dell_smbios_send_request(4, 11); - ret = buffer->output[0]; - dell_smbios_release_buffer(); - - return dell_smbios_error(ret); + return ret; } static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old) @@ -1345,7 +1281,6 @@ static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old) static int kbd_set_token_bit(u8 bit) { - struct calling_interface_buffer *buffer; struct calling_interface_token *token; int ret; @@ -1356,19 +1291,14 @@ static int kbd_set_token_bit(u8 bit) if (!token) return -EINVAL; - buffer = dell_smbios_get_buffer(); - buffer->input[0] = token->location; - buffer->input[1] = token->value; - dell_smbios_send_request(1, 0); - ret = buffer->output[0]; - dell_smbios_release_buffer(); + dell_set_arguments(token->location, token->value, 0, 0); + ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_STD); - return dell_smbios_error(ret); + return ret; } static int kbd_get_token_bit(u8 bit) { - struct calling_interface_buffer *buffer; struct calling_interface_token *token; int ret; int val; @@ -1380,15 +1310,12 @@ static int kbd_get_token_bit(u8 bit) if (!token) return -EINVAL; - buffer = dell_smbios_get_buffer(); - buffer->input[0] = token->location; - dell_smbios_send_request(0, 0); - ret = buffer->output[0]; + dell_set_arguments(token->location, 0, 0, 0); + ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_STD); val = buffer->output[1]; - dell_smbios_release_buffer(); if (ret) - return dell_smbios_error(ret); + return ret; return (val == token->value); } @@ -2102,7 +2029,6 @@ static struct notifier_block dell_laptop_notifier = { int dell_micmute_led_set(int state) { - struct calling_interface_buffer *buffer; struct calling_interface_token *token; if (state == 0) @@ -2115,11 +2041,8 @@ int dell_micmute_led_set(int state) if (!token) return -ENODEV; - buffer = dell_smbios_get_buffer(); - buffer->input[0] = token->location; - buffer->input[1] = token->value; - dell_smbios_send_request(1, 0); - dell_smbios_release_buffer(); + dell_set_arguments(token->location, token->value, 0, 0); + dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_STD); return state; } @@ -2127,7 +2050,6 @@ EXPORT_SYMBOL_GPL(dell_micmute_led_set); static int __init dell_init(void) { - struct calling_interface_buffer *buffer; struct calling_interface_token *token; int max_intensity = 0; int ret; @@ -2151,6 +2073,11 @@ static int __init dell_init(void) if (ret) goto fail_platform_device2; + buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL); + if (!buffer) + goto fail_buffer; + + ret = dell_setup_rfkill(); if (ret) { @@ -2175,12 +2102,10 @@ static int __init dell_init(void) token = dell_smbios_find_token(BRIGHTNESS_TOKEN); if (token) { - buffer = dell_smbios_get_buffer(); - buffer->input[0] = token->location; - dell_smbios_send_request(0, 2); - if (buffer->output[0] == 0) + dell_set_arguments(token->location, 0, 0, 0); + ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_AC); + if (ret) max_intensity = buffer->output[3]; - dell_smbios_release_buffer(); } if (max_intensity) { @@ -2214,6 +2139,8 @@ static int __init dell_init(void) fail_get_brightness: backlight_device_unregister(dell_backlight_device); fail_backlight: + kfree(buffer); +fail_buffer: dell_cleanup_rfkill(); fail_rfkill: platform_device_del(platform_device); @@ -2233,6 +2160,7 @@ static void __exit dell_exit(void) touchpad_led_exit(); kbd_led_exit(); backlight_device_unregister(dell_backlight_device); + kfree(buffer); dell_cleanup_rfkill(); if (platform_device) { platform_device_unregister(platform_device); diff --git a/drivers/platform/x86/dell-smbios-smm.c b/drivers/platform/x86/dell-smbios-smm.c new file mode 100644 index 000000000000..89f65c4651a0 --- /dev/null +++ b/drivers/platform/x86/dell-smbios-smm.c @@ -0,0 +1,196 @@ +/* + * SMI methods for use with dell-smbios + * + * Copyright (c) Red Hat <mjg@redhat.com> + * Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com> + * Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com> + * Copyright (c) 2017 Dell Inc. + * + * 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. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/dmi.h> +#include <linux/gfp.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include "../../firmware/dcdbas.h" +#include "dell-smbios.h" + +static int da_command_address; +static int da_command_code; +static struct calling_interface_buffer *buffer; +struct platform_device *platform_device; +static DEFINE_MUTEX(smm_mutex); + +static const struct dmi_system_id dell_device_table[] __initconst = { + { + .ident = "Dell laptop", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/ + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/ + }, + }, + { + .ident = "Dell Computer Corporation", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), + }, + }, + { } +}; +MODULE_DEVICE_TABLE(dmi, dell_device_table); + +static void __init parse_da_table(const struct dmi_header *dm) +{ + struct calling_interface_structure *table = + container_of(dm, struct calling_interface_structure, header); + + /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least + * 6 bytes of entry + */ + if (dm->length < 17) + return; + + da_command_address = table->cmdIOAddress; + da_command_code = table->cmdIOCode; +} + +static void __init find_cmd_address(const struct dmi_header *dm, void *dummy) +{ + switch (dm->type) { + case 0xda: /* Calling interface */ + parse_da_table(dm); + break; + } +} + +int dell_smbios_smm_call(struct calling_interface_buffer *input) +{ + struct smi_cmd command; + size_t size; + + size = sizeof(struct calling_interface_buffer); + command.magic = SMI_CMD_MAGIC; + command.command_address = da_command_address; + command.command_code = da_command_code; + command.ebx = virt_to_phys(buffer); + command.ecx = 0x42534931; + + mutex_lock(&smm_mutex); + memcpy(buffer, input, size); + dcdbas_smi_request(&command); + memcpy(input, buffer, size); + mutex_unlock(&smm_mutex); + return 0; +} + +/* When enabled this indicates that SMM won't work */ +static bool test_wsmt_enabled(void) +{ + struct calling_interface_token *wsmt; + + /* if token doesn't exist, SMM will work */ + wsmt = dell_smbios_find_token(WSMT_EN_TOKEN); + if (!wsmt) + return false; + + /* If token exists, try to access over SMM but set a dummy return. + * - If WSMT disabled it will be overwritten by SMM + * - If WSMT enabled then dummy value will remain + */ + buffer->cmd_class = CLASS_TOKEN_READ; + buffer->cmd_select = SELECT_TOKEN_STD; + memset(buffer, 0, sizeof(struct calling_interface_buffer)); + buffer->input[0] = wsmt->location; + buffer->output[0] = 99; + dell_smbios_smm_call(buffer); + if (buffer->output[0] == 99) + return true; + + return false; +} + +static int __init dell_smbios_smm_init(void) +{ + int ret; + /* + * Allocate buffer below 4GB for SMI data--only 32-bit physical addr + * is passed to SMI handler. + */ + buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32); + if (!buffer) + return -ENOMEM; + + dmi_walk(find_cmd_address, NULL); + + if (test_wsmt_enabled()) { + pr_debug("Disabling due to WSMT enabled\n"); + ret = -ENODEV; + goto fail_wsmt; + } + + platform_device = platform_device_alloc("dell-smbios", 1); + if (!platform_device) { + ret = -ENOMEM; + goto fail_platform_device_alloc; + } + + ret = platform_device_add(platform_device); + if (ret) + goto fail_platform_device_add; + + ret = dell_smbios_register_device(&platform_device->dev, + &dell_smbios_smm_call); + if (ret) + goto fail_register; + + return 0; + +fail_register: + platform_device_del(platform_device); + +fail_platform_device_add: + platform_device_put(platform_device); + +fail_wsmt: +fail_platform_device_alloc: + free_page((unsigned long)buffer); + return ret; +} + +static void __exit dell_smbios_smm_exit(void) +{ + if (platform_device) { + dell_smbios_unregister_device(&platform_device->dev); + platform_device_unregister(platform_device); + free_page((unsigned long)buffer); + } +} + +subsys_initcall(dell_smbios_smm_init); +module_exit(dell_smbios_smm_exit); + +MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); +MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>"); +MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>"); +MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); +MODULE_DESCRIPTION("Dell SMBIOS communications over SMI"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/dell-smbios-wmi.c b/drivers/platform/x86/dell-smbios-wmi.c new file mode 100644 index 000000000000..0cab1f9c35af --- /dev/null +++ b/drivers/platform/x86/dell-smbios-wmi.c @@ -0,0 +1,272 @@ +/* + * WMI methods for use with dell-smbios + * + * Copyright (c) 2017 Dell Inc. + * + * 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. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/dmi.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/uaccess.h> +#include <linux/wmi.h> +#include "dell-smbios.h" +#include "dell-wmi-descriptor.h" + +static DEFINE_MUTEX(call_mutex); +static DEFINE_MUTEX(list_mutex); +static int wmi_supported; + +struct misc_bios_flags_structure { + struct dmi_header header; + u16 flags0; +} __packed; +#define FLAG_HAS_ACPI_WMI 0x02 + +#define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" + +struct wmi_smbios_priv { + struct dell_wmi_smbios_buffer *buf; + struct list_head list; + struct wmi_device *wdev; + struct device *child; + u32 req_buf_size; +}; +static LIST_HEAD(wmi_list); + +static inline struct wmi_smbios_priv *get_first_smbios_priv(void) +{ + return list_first_entry_or_null(&wmi_list, + struct wmi_smbios_priv, + list); +} + +static int run_smbios_call(struct wmi_device *wdev) +{ + struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; + struct wmi_smbios_priv *priv; + struct acpi_buffer input; + union acpi_object *obj; + acpi_status status; + + priv = dev_get_drvdata(&wdev->dev); + input.length = priv->req_buf_size - sizeof(u64); + input.pointer = &priv->buf->std; + + dev_dbg(&wdev->dev, "evaluating: %u/%u [%x,%x,%x,%x]\n", + priv->buf->std.cmd_class, priv->buf->std.cmd_select, + priv->buf->std.input[0], priv->buf->std.input[1], + priv->buf->std.input[2], priv->buf->std.input[3]); + + status = wmidev_evaluate_method(wdev, 0, 1, &input, &output); + if (ACPI_FAILURE(status)) + return -EIO; + obj = (union acpi_object *)output.pointer; + if (obj->type != ACPI_TYPE_BUFFER) { + dev_dbg(&wdev->dev, "received type: %d\n", obj->type); + if (obj->type == ACPI_TYPE_INTEGER) + dev_dbg(&wdev->dev, "SMBIOS call failed: %llu\n", + obj->integer.value); + return -EIO; + } + memcpy(&priv->buf->std, obj->buffer.pointer, obj->buffer.length); + dev_dbg(&wdev->dev, "result: [%08x,%08x,%08x,%08x]\n", + priv->buf->std.output[0], priv->buf->std.output[1], + priv->buf->std.output[2], priv->buf->std.output[3]); + + return 0; +} + +int dell_smbios_wmi_call(struct calling_interface_buffer *buffer) +{ + struct wmi_smbios_priv *priv; + size_t difference; + size_t size; + int ret; + + mutex_lock(&call_mutex); + priv = get_first_smbios_priv(); + if (!priv) { + ret = -ENODEV; + goto out_wmi_call; + } + + size = sizeof(struct calling_interface_buffer); + difference = priv->req_buf_size - sizeof(u64) - size; + + memset(&priv->buf->ext, 0, difference); + memcpy(&priv->buf->std, buffer, size); + ret = run_smbios_call(priv->wdev); + memcpy(buffer, &priv->buf->std, size); +out_wmi_call: + mutex_unlock(&call_mutex); + + return ret; +} + +static long dell_smbios_wmi_filter(struct wmi_device *wdev, unsigned int cmd, + struct wmi_ioctl_buffer *arg) +{ + struct wmi_smbios_priv *priv; + int ret = 0; + + switch (cmd) { + case DELL_WMI_SMBIOS_CMD: + mutex_lock(&call_mutex); + priv = dev_get_drvdata(&wdev->dev); + if (!priv) { + ret = -ENODEV; + goto fail_smbios_cmd; + } + memcpy(priv->buf, arg, priv->req_buf_size); + if (dell_smbios_call_filter(&wdev->dev, &priv->buf->std)) { + dev_err(&wdev->dev, "Invalid call %d/%d:%8x\n", + priv->buf->std.cmd_class, + priv->buf->std.cmd_select, + priv->buf->std.input[0]); + ret = -EFAULT; + goto fail_smbios_cmd; + } + ret = run_smbios_call(priv->wdev); + if (ret) + goto fail_smbios_cmd; + memcpy(arg, priv->buf, priv->req_buf_size); +fail_smbios_cmd: + mutex_unlock(&call_mutex); + break; + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +static int dell_smbios_wmi_probe(struct wmi_device *wdev) +{ + struct wmi_smbios_priv *priv; + int count; + int ret; + + ret = dell_wmi_get_descriptor_valid(); + if (ret) + return ret; + + priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_smbios_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* WMI buffer size will be either 4k or 32k depending on machine */ + if (!dell_wmi_get_size(&priv->req_buf_size)) + return -EPROBE_DEFER; + + /* add in the length object we will use internally with ioctl */ + priv->req_buf_size += sizeof(u64); + ret = set_required_buffer_size(wdev, priv->req_buf_size); + if (ret) + return ret; + + count = get_order(priv->req_buf_size); + priv->buf = (void *)__get_free_pages(GFP_KERNEL, count); + if (!priv->buf) + return -ENOMEM; + + /* ID is used by dell-smbios to set priority of drivers */ + wdev->dev.id = 1; + ret = dell_smbios_register_device(&wdev->dev, &dell_smbios_wmi_call); + if (ret) + goto fail_register; + + priv->wdev = wdev; + dev_set_drvdata(&wdev->dev, priv); + mutex_lock(&list_mutex); + list_add_tail(&priv->list, &wmi_list); + mutex_unlock(&list_mutex); + + return 0; + +fail_register: + free_pages((unsigned long)priv->buf, count); + return ret; +} + +static int dell_smbios_wmi_remove(struct wmi_device *wdev) +{ + struct wmi_smbios_priv *priv = dev_get_drvdata(&wdev->dev); + int count; + + mutex_lock(&call_mutex); + mutex_lock(&list_mutex); + list_del(&priv->list); + mutex_unlock(&list_mutex); + dell_smbios_unregister_device(&wdev->dev); + count = get_order(priv->req_buf_size); + free_pages((unsigned long)priv->buf, count); + mutex_unlock(&call_mutex); + return 0; +} + +static const struct wmi_device_id dell_smbios_wmi_id_table[] = { + { .guid_string = DELL_WMI_SMBIOS_GUID }, + { }, +}; + +static void __init parse_b1_table(const struct dmi_header *dm) +{ + struct misc_bios_flags_structure *flags = + container_of(dm, struct misc_bios_flags_structure, header); + + /* 4 bytes header, 8 bytes flags */ + if (dm->length < 12) + return; + if (dm->handle != 0xb100) + return; + if ((flags->flags0 & FLAG_HAS_ACPI_WMI)) + wmi_supported = 1; +} + +static void __init find_b1(const struct dmi_header *dm, void *dummy) +{ + switch (dm->type) { + case 0xb1: /* misc bios flags */ + parse_b1_table(dm); + break; + } +} + +static struct wmi_driver dell_smbios_wmi_driver = { + .driver = { + .name = "dell-smbios", + }, + .probe = dell_smbios_wmi_probe, + .remove = dell_smbios_wmi_remove, + .id_table = dell_smbios_wmi_id_table, + .filter_callback = dell_smbios_wmi_filter, +}; + +static int __init init_dell_smbios_wmi(void) +{ + dmi_walk(find_b1, NULL); + + if (!wmi_supported) + return -ENODEV; + + return wmi_driver_register(&dell_smbios_wmi_driver); +} + +static void __exit exit_dell_smbios_wmi(void) +{ + wmi_driver_unregister(&dell_smbios_wmi_driver); +} + +module_init(init_dell_smbios_wmi); +module_exit(exit_dell_smbios_wmi); + +MODULE_ALIAS("wmi:" DELL_WMI_SMBIOS_GUID); +MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); +MODULE_DESCRIPTION("Dell SMBIOS communications over WMI"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/dell-smbios.c b/drivers/platform/x86/dell-smbios.c index 0a5723468bff..6a60db515bda 100644 --- a/drivers/platform/x86/dell-smbios.c +++ b/drivers/platform/x86/dell-smbios.c @@ -12,33 +12,119 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> #include <linux/module.h> +#include <linux/capability.h> #include <linux/dmi.h> #include <linux/err.h> -#include <linux/gfp.h> #include <linux/mutex.h> +#include <linux/platform_device.h> #include <linux/slab.h> -#include <linux/io.h> -#include "../../firmware/dcdbas.h" #include "dell-smbios.h" -struct calling_interface_structure { - struct dmi_header header; - u16 cmdIOAddress; - u8 cmdIOCode; - u32 supportedCmds; - struct calling_interface_token tokens[]; -} __packed; - -static struct calling_interface_buffer *buffer; -static DEFINE_MUTEX(buffer_mutex); - -static int da_command_address; -static int da_command_code; +static u32 da_supported_commands; static int da_num_tokens; +static struct platform_device *platform_device; static struct calling_interface_token *da_tokens; +static struct device_attribute *token_location_attrs; +static struct device_attribute *token_value_attrs; +static struct attribute **token_attrs; +static DEFINE_MUTEX(smbios_mutex); + +struct smbios_device { + struct list_head list; + struct device *device; + int (*call_fn)(struct calling_interface_buffer *); +}; + +struct smbios_call { + u32 need_capability; + int cmd_class; + int cmd_select; +}; + +/* calls that are whitelisted for given capabilities */ +static struct smbios_call call_whitelist[] = { + /* generally tokens are allowed, but may be further filtered or + * restricted by token blacklist or whitelist + */ + {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_STD}, + {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_AC}, + {CAP_SYS_ADMIN, CLASS_TOKEN_READ, SELECT_TOKEN_BAT}, + {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_STD}, + {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_AC}, + {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT}, + /* used by userspace: fwupdate */ + {CAP_SYS_ADMIN, CLASS_ADMIN_PROP, SELECT_ADMIN_PROP}, + /* used by userspace: fwupd */ + {CAP_SYS_ADMIN, CLASS_INFO, SELECT_DOCK}, + {CAP_SYS_ADMIN, CLASS_FLASH_INTERFACE, SELECT_FLASH_INTERFACE}, +}; + +/* calls that are explicitly blacklisted */ +static struct smbios_call call_blacklist[] = { + {0x0000, 01, 07}, /* manufacturing use */ + {0x0000, 06, 05}, /* manufacturing use */ + {0x0000, 11, 03}, /* write once */ + {0x0000, 11, 07}, /* write once */ + {0x0000, 11, 11}, /* write once */ + {0x0000, 19, -1}, /* diagnostics */ + /* handled by kernel: dell-laptop */ + {0x0000, CLASS_INFO, SELECT_RFKILL}, + {0x0000, CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT}, +}; + +struct token_range { + u32 need_capability; + u16 min; + u16 max; +}; + +/* tokens that are whitelisted for given capabilities */ +static struct token_range token_whitelist[] = { + /* used by userspace: fwupdate */ + {CAP_SYS_ADMIN, CAPSULE_EN_TOKEN, CAPSULE_DIS_TOKEN}, + /* can indicate to userspace that WMI is needed */ + {0x0000, WSMT_EN_TOKEN, WSMT_DIS_TOKEN} +}; + +/* tokens that are explicitly blacklisted */ +static struct token_range token_blacklist[] = { + {0x0000, 0x0058, 0x0059}, /* ME use */ + {0x0000, 0x00CD, 0x00D0}, /* raid shadow copy */ + {0x0000, 0x013A, 0x01FF}, /* sata shadow copy */ + {0x0000, 0x0175, 0x0176}, /* write once */ + {0x0000, 0x0195, 0x0197}, /* diagnostics */ + {0x0000, 0x01DC, 0x01DD}, /* manufacturing use */ + {0x0000, 0x027D, 0x0284}, /* diagnostics */ + {0x0000, 0x02E3, 0x02E3}, /* manufacturing use */ + {0x0000, 0x02FF, 0x02FF}, /* manufacturing use */ + {0x0000, 0x0300, 0x0302}, /* manufacturing use */ + {0x0000, 0x0325, 0x0326}, /* manufacturing use */ + {0x0000, 0x0332, 0x0335}, /* fan control */ + {0x0000, 0x0350, 0x0350}, /* manufacturing use */ + {0x0000, 0x0363, 0x0363}, /* manufacturing use */ + {0x0000, 0x0368, 0x0368}, /* manufacturing use */ + {0x0000, 0x03F6, 0x03F7}, /* manufacturing use */ + {0x0000, 0x049E, 0x049F}, /* manufacturing use */ + {0x0000, 0x04A0, 0x04A3}, /* disagnostics */ + {0x0000, 0x04E6, 0x04E7}, /* manufacturing use */ + {0x0000, 0x4000, 0x7FFF}, /* internal BIOS use */ + {0x0000, 0x9000, 0x9001}, /* internal BIOS use */ + {0x0000, 0xA000, 0xBFFF}, /* write only */ + {0x0000, 0xEFF0, 0xEFFF}, /* internal BIOS use */ + /* handled by kernel: dell-laptop */ + {0x0000, BRIGHTNESS_TOKEN, BRIGHTNESS_TOKEN}, + {0x0000, KBD_LED_OFF_TOKEN, KBD_LED_AUTO_TOKEN}, + {0x0000, KBD_LED_AC_TOKEN, KBD_LED_AC_TOKEN}, + {0x0000, KBD_LED_AUTO_25_TOKEN, KBD_LED_AUTO_75_TOKEN}, + {0x0000, KBD_LED_AUTO_100_TOKEN, KBD_LED_AUTO_100_TOKEN}, + {0x0000, GLOBAL_MIC_MUTE_ENABLE, GLOBAL_MIC_MUTE_DISABLE}, +}; + +static LIST_HEAD(smbios_device_list); int dell_smbios_error(int value) { @@ -55,42 +141,175 @@ int dell_smbios_error(int value) } EXPORT_SYMBOL_GPL(dell_smbios_error); -struct calling_interface_buffer *dell_smbios_get_buffer(void) +int dell_smbios_register_device(struct device *d, void *call_fn) { - mutex_lock(&buffer_mutex); - dell_smbios_clear_buffer(); - return buffer; + struct smbios_device *priv; + + priv = devm_kzalloc(d, sizeof(struct smbios_device), GFP_KERNEL); + if (!priv) + return -ENOMEM; + get_device(d); + priv->device = d; + priv->call_fn = call_fn; + mutex_lock(&smbios_mutex); + list_add_tail(&priv->list, &smbios_device_list); + mutex_unlock(&smbios_mutex); + dev_dbg(d, "Added device: %s\n", d->driver->name); + return 0; } -EXPORT_SYMBOL_GPL(dell_smbios_get_buffer); +EXPORT_SYMBOL_GPL(dell_smbios_register_device); -void dell_smbios_clear_buffer(void) +void dell_smbios_unregister_device(struct device *d) { - memset(buffer, 0, sizeof(struct calling_interface_buffer)); + struct smbios_device *priv; + + mutex_lock(&smbios_mutex); + list_for_each_entry(priv, &smbios_device_list, list) { + if (priv->device == d) { + list_del(&priv->list); + put_device(d); + break; + } + } + mutex_unlock(&smbios_mutex); + dev_dbg(d, "Remove device: %s\n", d->driver->name); } -EXPORT_SYMBOL_GPL(dell_smbios_clear_buffer); +EXPORT_SYMBOL_GPL(dell_smbios_unregister_device); -void dell_smbios_release_buffer(void) +int dell_smbios_call_filter(struct device *d, + struct calling_interface_buffer *buffer) { - mutex_unlock(&buffer_mutex); + u16 t = 0; + int i; + + /* can't make calls over 30 */ + if (buffer->cmd_class > 30) { + dev_dbg(d, "class too big: %u\n", buffer->cmd_class); + return -EINVAL; + } + + /* supported calls on the particular system */ + if (!(da_supported_commands & (1 << buffer->cmd_class))) { + dev_dbg(d, "invalid command, supported commands: 0x%8x\n", + da_supported_commands); + return -EINVAL; + } + + /* match against call blacklist */ + for (i = 0; i < ARRAY_SIZE(call_blacklist); i++) { + if (buffer->cmd_class != call_blacklist[i].cmd_class) + continue; + if (buffer->cmd_select != call_blacklist[i].cmd_select && + call_blacklist[i].cmd_select != -1) + continue; + dev_dbg(d, "blacklisted command: %u/%u\n", + buffer->cmd_class, buffer->cmd_select); + return -EINVAL; + } + + /* if a token call, find token ID */ + + if ((buffer->cmd_class == CLASS_TOKEN_READ || + buffer->cmd_class == CLASS_TOKEN_WRITE) && + buffer->cmd_select < 3) { + /* find the matching token ID */ + for (i = 0; i < da_num_tokens; i++) { + if (da_tokens[i].location != buffer->input[0]) + continue; + t = da_tokens[i].tokenID; + break; + } + + /* token call; but token didn't exist */ + if (!t) { + dev_dbg(d, "token at location %04x doesn't exist\n", + buffer->input[0]); + return -EINVAL; + } + + /* match against token blacklist */ + for (i = 0; i < ARRAY_SIZE(token_blacklist); i++) { + if (!token_blacklist[i].min || !token_blacklist[i].max) + continue; + if (t >= token_blacklist[i].min && + t <= token_blacklist[i].max) + return -EINVAL; + } + + /* match against token whitelist */ + for (i = 0; i < ARRAY_SIZE(token_whitelist); i++) { + if (!token_whitelist[i].min || !token_whitelist[i].max) + continue; + if (t < token_whitelist[i].min || + t > token_whitelist[i].max) + continue; + if (!token_whitelist[i].need_capability || + capable(token_whitelist[i].need_capability)) { + dev_dbg(d, "whitelisted token: %x\n", t); + return 0; + } + + } + } + /* match against call whitelist */ + for (i = 0; i < ARRAY_SIZE(call_whitelist); i++) { + if (buffer->cmd_class != call_whitelist[i].cmd_class) + continue; + if (buffer->cmd_select != call_whitelist[i].cmd_select) + continue; + if (!call_whitelist[i].need_capability || + capable(call_whitelist[i].need_capability)) { + dev_dbg(d, "whitelisted capable command: %u/%u\n", + buffer->cmd_class, buffer->cmd_select); + return 0; + } + dev_dbg(d, "missing capability %d for %u/%u\n", + call_whitelist[i].need_capability, + buffer->cmd_class, buffer->cmd_select); + + } + + /* not in a whitelist, only allow processes with capabilities */ + if (capable(CAP_SYS_RAWIO)) { + dev_dbg(d, "Allowing %u/%u due to CAP_SYS_RAWIO\n", + buffer->cmd_class, buffer->cmd_select); + return 0; + } + + return -EACCES; } -EXPORT_SYMBOL_GPL(dell_smbios_release_buffer); +EXPORT_SYMBOL_GPL(dell_smbios_call_filter); -void dell_smbios_send_request(int class, int select) +int dell_smbios_call(struct calling_interface_buffer *buffer) { - struct smi_cmd command; + int (*call_fn)(struct calling_interface_buffer *) = NULL; + struct device *selected_dev = NULL; + struct smbios_device *priv; + int ret; - command.magic = SMI_CMD_MAGIC; - command.command_address = da_command_address; - command.command_code = da_command_code; - command.ebx = virt_to_phys(buffer); - command.ecx = 0x42534931; + mutex_lock(&smbios_mutex); + list_for_each_entry(priv, &smbios_device_list, list) { + if (!selected_dev || priv->device->id >= selected_dev->id) { + dev_dbg(priv->device, "Trying device ID: %d\n", + priv->device->id); + call_fn = priv->call_fn; + selected_dev = priv->device; + } + } + + if (!selected_dev) { + ret = -ENODEV; + pr_err("No dell-smbios drivers are loaded\n"); + goto out_smbios_call; + } - buffer->class = class; - buffer->select = select; + ret = call_fn(buffer); - dcdbas_smi_request(&command); +out_smbios_call: + mutex_unlock(&smbios_mutex); + return ret; } -EXPORT_SYMBOL_GPL(dell_smbios_send_request); +EXPORT_SYMBOL_GPL(dell_smbios_call); struct calling_interface_token *dell_smbios_find_token(int tokenid) { @@ -139,8 +358,7 @@ static void __init parse_da_table(const struct dmi_header *dm) if (dm->length < 17) return; - da_command_address = table->cmdIOAddress; - da_command_code = table->cmdIOCode; + da_supported_commands = table->supportedCmds; new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) * sizeof(struct calling_interface_token), @@ -156,6 +374,27 @@ static void __init parse_da_table(const struct dmi_header *dm) da_num_tokens += tokens; } +static void zero_duplicates(struct device *dev) +{ + int i, j; + + for (i = 0; i < da_num_tokens; i++) { + if (da_tokens[i].tokenID == 0) + continue; + for (j = i+1; j < da_num_tokens; j++) { + if (da_tokens[j].tokenID == 0) + continue; + if (da_tokens[i].tokenID == da_tokens[j].tokenID) { + dev_dbg(dev, "Zeroing dup token ID %x(%x/%x)\n", + da_tokens[j].tokenID, + da_tokens[j].location, + da_tokens[j].value); + da_tokens[j].tokenID = 0; + } + } + } +} + static void __init find_tokens(const struct dmi_header *dm, void *dummy) { switch (dm->type) { @@ -169,10 +408,160 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy) } } +static int match_attribute(struct device *dev, + struct device_attribute *attr) +{ + int i; + + for (i = 0; i < da_num_tokens * 2; i++) { + if (!token_attrs[i]) + continue; + if (strcmp(token_attrs[i]->name, attr->attr.name) == 0) + return i/2; + } + dev_dbg(dev, "couldn't match: %s\n", attr->attr.name); + return -EINVAL; +} + +static ssize_t location_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + i = match_attribute(dev, attr); + if (i > 0) + return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].location); + return 0; +} + +static ssize_t value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + i = match_attribute(dev, attr); + if (i > 0) + return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].value); + return 0; +} + +static struct attribute_group smbios_attribute_group = { + .name = "tokens" +}; + +static struct platform_driver platform_driver = { + .driver = { + .name = "dell-smbios", + }, +}; + +static int build_tokens_sysfs(struct platform_device *dev) +{ + char *location_name; + char *value_name; + size_t size; + int ret; + int i, j; + + /* (number of tokens + 1 for null terminated */ + size = sizeof(struct device_attribute) * (da_num_tokens + 1); + token_location_attrs = kzalloc(size, GFP_KERNEL); + if (!token_location_attrs) + return -ENOMEM; + token_value_attrs = kzalloc(size, GFP_KERNEL); + if (!token_value_attrs) + goto out_allocate_value; + + /* need to store both location and value + terminator*/ + size = sizeof(struct attribute *) * ((2 * da_num_tokens) + 1); + token_attrs = kzalloc(size, GFP_KERNEL); + if (!token_attrs) + goto out_allocate_attrs; + + for (i = 0, j = 0; i < da_num_tokens; i++) { + /* skip empty */ + if (da_tokens[i].tokenID == 0) + continue; + /* add location */ + location_name = kasprintf(GFP_KERNEL, "%04x_location", + da_tokens[i].tokenID); + if (location_name == NULL) + goto out_unwind_strings; + sysfs_attr_init(&token_location_attrs[i].attr); + token_location_attrs[i].attr.name = location_name; + token_location_attrs[i].attr.mode = 0444; + token_location_attrs[i].show = location_show; + token_attrs[j++] = &token_location_attrs[i].attr; + + /* add value */ + value_name = kasprintf(GFP_KERNEL, "%04x_value", + da_tokens[i].tokenID); + if (value_name == NULL) + goto loop_fail_create_value; + sysfs_attr_init(&token_value_attrs[i].attr); + token_value_attrs[i].attr.name = value_name; + token_value_attrs[i].attr.mode = 0444; + token_value_attrs[i].show = value_show; + token_attrs[j++] = &token_value_attrs[i].attr; + continue; + +loop_fail_create_value: + kfree(value_name); + goto out_unwind_strings; + } + smbios_attribute_group.attrs = token_attrs; + + ret = sysfs_create_group(&dev->dev.kobj, &smbios_attribute_group); + if (ret) + goto out_unwind_strings; + return 0; + +out_unwind_strings: + for (i = i-1; i > 0; i--) { + kfree(token_location_attrs[i].attr.name); + kfree(token_value_attrs[i].attr.name); + } + kfree(token_attrs); +out_allocate_attrs: + kfree(token_value_attrs); +out_allocate_value: + kfree(token_location_attrs); + + return -ENOMEM; +} + +static void free_group(struct platform_device *pdev) +{ + int i; + + sysfs_remove_group(&pdev->dev.kobj, + &smbios_attribute_group); + for (i = 0; i < da_num_tokens; i++) { + kfree(token_location_attrs[i].attr.name); + kfree(token_value_attrs[i].attr.name); + } + kfree(token_attrs); + kfree(token_value_attrs); + kfree(token_location_attrs); +} + static int __init dell_smbios_init(void) { + const struct dmi_device *valid; int ret; + valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL); + if (!valid) { + pr_err("Unable to run on non-Dell system\n"); + return -ENODEV; + } + dmi_walk(find_tokens, NULL); if (!da_tokens) { @@ -180,27 +569,52 @@ static int __init dell_smbios_init(void) return -ENODEV; } - /* - * Allocate buffer below 4GB for SMI data--only 32-bit physical addr - * is passed to SMI handler. - */ - buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32); - if (!buffer) { + ret = platform_driver_register(&platform_driver); + if (ret) + goto fail_platform_driver; + + platform_device = platform_device_alloc("dell-smbios", 0); + if (!platform_device) { ret = -ENOMEM; - goto fail_buffer; + goto fail_platform_device_alloc; } + ret = platform_device_add(platform_device); + if (ret) + goto fail_platform_device_add; + + /* duplicate tokens will cause problems building sysfs files */ + zero_duplicates(&platform_device->dev); + + ret = build_tokens_sysfs(platform_device); + if (ret) + goto fail_create_group; return 0; -fail_buffer: +fail_create_group: + platform_device_del(platform_device); + +fail_platform_device_add: + platform_device_put(platform_device); + +fail_platform_device_alloc: + platform_driver_unregister(&platform_driver); + +fail_platform_driver: kfree(da_tokens); return ret; } static void __exit dell_smbios_exit(void) { + mutex_lock(&smbios_mutex); + if (platform_device) { + free_group(platform_device); + platform_device_unregister(platform_device); + platform_driver_unregister(&platform_driver); + } kfree(da_tokens); - free_page((unsigned long)buffer); + mutex_unlock(&smbios_mutex); } subsys_initcall(dell_smbios_init); diff --git a/drivers/platform/x86/dell-smbios.h b/drivers/platform/x86/dell-smbios.h index 45cbc2292cd3..138d478d9adc 100644 --- a/drivers/platform/x86/dell-smbios.h +++ b/drivers/platform/x86/dell-smbios.h @@ -16,17 +16,29 @@ #ifndef _DELL_SMBIOS_H_ #define _DELL_SMBIOS_H_ -struct notifier_block; +#include <linux/device.h> +#include <uapi/linux/wmi.h> -/* This structure will be modified by the firmware when we enter - * system management mode, hence the volatiles */ +/* Classes and selects used only in kernel drivers */ +#define CLASS_KBD_BACKLIGHT 4 +#define SELECT_KBD_BACKLIGHT 11 -struct calling_interface_buffer { - u16 class; - u16 select; - volatile u32 input[4]; - volatile u32 output[4]; -} __packed; +/* Tokens used in kernel drivers, any of these + * should be filtered from userspace access + */ +#define BRIGHTNESS_TOKEN 0x007d +#define KBD_LED_AC_TOKEN 0x0451 +#define KBD_LED_OFF_TOKEN 0x01E1 +#define KBD_LED_ON_TOKEN 0x01E2 +#define KBD_LED_AUTO_TOKEN 0x01E3 +#define KBD_LED_AUTO_25_TOKEN 0x02EA +#define KBD_LED_AUTO_50_TOKEN 0x02EB +#define KBD_LED_AUTO_75_TOKEN 0x02EC +#define KBD_LED_AUTO_100_TOKEN 0x02F6 +#define GLOBAL_MIC_MUTE_ENABLE 0x0364 +#define GLOBAL_MIC_MUTE_DISABLE 0x0365 + +struct notifier_block; struct calling_interface_token { u16 tokenID; @@ -37,12 +49,21 @@ struct calling_interface_token { }; }; -int dell_smbios_error(int value); +struct calling_interface_structure { + struct dmi_header header; + u16 cmdIOAddress; + u8 cmdIOCode; + u32 supportedCmds; + struct calling_interface_token tokens[]; +} __packed; -struct calling_interface_buffer *dell_smbios_get_buffer(void); -void dell_smbios_clear_buffer(void); -void dell_smbios_release_buffer(void); -void dell_smbios_send_request(int class, int select); +int dell_smbios_register_device(struct device *d, void *call_fn); +void dell_smbios_unregister_device(struct device *d); + +int dell_smbios_error(int value); +int dell_smbios_call_filter(struct device *d, + struct calling_interface_buffer *buffer); +int dell_smbios_call(struct calling_interface_buffer *buffer); struct calling_interface_token *dell_smbios_find_token(int tokenid); diff --git a/drivers/platform/x86/dell-smo8800.c b/drivers/platform/x86/dell-smo8800.c index 37e646034ef8..1d87237bc731 100644 --- a/drivers/platform/x86/dell-smo8800.c +++ b/drivers/platform/x86/dell-smo8800.c @@ -90,7 +90,7 @@ static ssize_t smo8800_misc_read(struct file *file, char __user *buf, struct smo8800_device, miscdev); u32 data = 0; - unsigned char byte_data = 0; + unsigned char byte_data; ssize_t retval = 1; if (count < 1) @@ -103,7 +103,6 @@ static ssize_t smo8800_misc_read(struct file *file, char __user *buf, if (retval) return retval; - byte_data = 1; retval = 1; if (data < 255) diff --git a/drivers/platform/x86/dell-wmi-descriptor.c b/drivers/platform/x86/dell-wmi-descriptor.c new file mode 100644 index 000000000000..4dfef1f53481 --- /dev/null +++ b/drivers/platform/x86/dell-wmi-descriptor.c @@ -0,0 +1,191 @@ +/* + * Dell WMI descriptor driver + * + * Copyright (C) 2017 Dell Inc. All Rights Reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/acpi.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/wmi.h> +#include "dell-wmi-descriptor.h" + +#define DELL_WMI_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492" + +struct descriptor_priv { + struct list_head list; + u32 interface_version; + u32 size; +}; +static int descriptor_valid = -EPROBE_DEFER; +static LIST_HEAD(wmi_list); +static DEFINE_MUTEX(list_mutex); + +int dell_wmi_get_descriptor_valid(void) +{ + if (!wmi_has_guid(DELL_WMI_DESCRIPTOR_GUID)) + return -ENODEV; + + return descriptor_valid; +} +EXPORT_SYMBOL_GPL(dell_wmi_get_descriptor_valid); + +bool dell_wmi_get_interface_version(u32 *version) +{ + struct descriptor_priv *priv; + bool ret = false; + + mutex_lock(&list_mutex); + priv = list_first_entry_or_null(&wmi_list, + struct descriptor_priv, + list); + if (priv) { + *version = priv->interface_version; + ret = true; + } + mutex_unlock(&list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(dell_wmi_get_interface_version); + +bool dell_wmi_get_size(u32 *size) +{ + struct descriptor_priv *priv; + bool ret = false; + + mutex_lock(&list_mutex); + priv = list_first_entry_or_null(&wmi_list, + struct descriptor_priv, + list); + if (priv) { + *size = priv->size; + ret = true; + } + mutex_unlock(&list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(dell_wmi_get_size); + +/* + * Descriptor buffer is 128 byte long and contains: + * + * Name Offset Length Value + * Vendor Signature 0 4 "DELL" + * Object Signature 4 4 " WMI" + * WMI Interface Version 8 4 <version> + * WMI buffer length 12 4 <length> + */ +static int dell_wmi_descriptor_probe(struct wmi_device *wdev) +{ + union acpi_object *obj = NULL; + struct descriptor_priv *priv; + u32 *buffer; + int ret; + + obj = wmidev_block_query(wdev, 0); + if (!obj) { + dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n"); + ret = -EIO; + goto out; + } + + if (obj->type != ACPI_TYPE_BUFFER) { + dev_err(&wdev->dev, "Dell descriptor has wrong type\n"); + ret = -EINVAL; + descriptor_valid = ret; + goto out; + } + + /* Although it's not technically a failure, this would lead to + * unexpected behavior + */ + if (obj->buffer.length != 128) { + dev_err(&wdev->dev, + "Dell descriptor buffer has unexpected length (%d)\n", + obj->buffer.length); + ret = -EINVAL; + descriptor_valid = ret; + goto out; + } + + buffer = (u32 *)obj->buffer.pointer; + + if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) { + dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n", + buffer); + ret = -EINVAL; + descriptor_valid = ret; + goto out; + } + descriptor_valid = 0; + + if (buffer[2] != 0 && buffer[2] != 1) + dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n", + (unsigned long) buffer[2]); + + priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv), + GFP_KERNEL); + + if (!priv) { + ret = -ENOMEM; + goto out; + } + + priv->interface_version = buffer[2]; + priv->size = buffer[3]; + ret = 0; + dev_set_drvdata(&wdev->dev, priv); + mutex_lock(&list_mutex); + list_add_tail(&priv->list, &wmi_list); + mutex_unlock(&list_mutex); + + dev_dbg(&wdev->dev, "Detected Dell WMI interface version %lu and buffer size %lu\n", + (unsigned long) priv->interface_version, + (unsigned long) priv->size); + +out: + kfree(obj); + return ret; +} + +static int dell_wmi_descriptor_remove(struct wmi_device *wdev) +{ + struct descriptor_priv *priv = dev_get_drvdata(&wdev->dev); + + mutex_lock(&list_mutex); + list_del(&priv->list); + mutex_unlock(&list_mutex); + return 0; +} + +static const struct wmi_device_id dell_wmi_descriptor_id_table[] = { + { .guid_string = DELL_WMI_DESCRIPTOR_GUID }, + { }, +}; + +static struct wmi_driver dell_wmi_descriptor_driver = { + .driver = { + .name = "dell-wmi-descriptor", + }, + .probe = dell_wmi_descriptor_probe, + .remove = dell_wmi_descriptor_remove, + .id_table = dell_wmi_descriptor_id_table, +}; + +module_wmi_driver(dell_wmi_descriptor_driver); + +MODULE_ALIAS("wmi:" DELL_WMI_DESCRIPTOR_GUID); +MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); +MODULE_DESCRIPTION("Dell WMI descriptor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/dell-wmi-descriptor.h b/drivers/platform/x86/dell-wmi-descriptor.h new file mode 100644 index 000000000000..1e8cb96ffd78 --- /dev/null +++ b/drivers/platform/x86/dell-wmi-descriptor.h @@ -0,0 +1,27 @@ +/* + * Dell WMI descriptor driver + * + * Copyright (c) 2017 Dell Inc. + * + * 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. + */ + +#ifndef _DELL_WMI_DESCRIPTOR_H_ +#define _DELL_WMI_DESCRIPTOR_H_ + +#include <linux/wmi.h> + +/* possible return values: + * -ENODEV: Descriptor GUID missing from WMI bus + * -EPROBE_DEFER: probing for dell-wmi-descriptor not yet run + * 0: valid descriptor, successfully probed + * < 0: invalid descriptor, don't probe dependent devices + */ +int dell_wmi_get_descriptor_valid(void); + +bool dell_wmi_get_interface_version(u32 *version); +bool dell_wmi_get_size(u32 *size); + +#endif diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 28d9f8696081..39d2f4518483 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -39,6 +39,7 @@ #include <linux/wmi.h> #include <acpi/video.h> #include "dell-smbios.h" +#include "dell-wmi-descriptor.h" MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>"); @@ -46,12 +47,10 @@ MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver"); MODULE_LICENSE("GPL"); #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492" -#define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492" static bool wmi_requires_smbios_request; MODULE_ALIAS("wmi:"DELL_EVENT_GUID); -MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID); struct dell_wmi_priv { struct input_dev *input_dev; @@ -619,78 +618,6 @@ static void dell_wmi_input_destroy(struct wmi_device *wdev) } /* - * Descriptor buffer is 128 byte long and contains: - * - * Name Offset Length Value - * Vendor Signature 0 4 "DELL" - * Object Signature 4 4 " WMI" - * WMI Interface Version 8 4 <version> - * WMI buffer length 12 4 4096 - */ -static int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev) -{ - struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); - union acpi_object *obj = NULL; - struct wmi_device *desc_dev; - u32 *buffer; - int ret; - - desc_dev = wmidev_get_other_guid(wdev, DELL_DESCRIPTOR_GUID); - if (!desc_dev) { - dev_err(&wdev->dev, "Dell WMI descriptor does not exist\n"); - return -ENODEV; - } - - obj = wmidev_block_query(desc_dev, 0); - if (!obj) { - dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n"); - ret = -EIO; - goto out; - } - - if (obj->type != ACPI_TYPE_BUFFER) { - dev_err(&wdev->dev, "Dell descriptor has wrong type\n"); - ret = -EINVAL; - goto out; - } - - if (obj->buffer.length != 128) { - dev_err(&wdev->dev, - "Dell descriptor buffer has invalid length (%d)\n", - obj->buffer.length); - if (obj->buffer.length < 16) { - ret = -EINVAL; - goto out; - } - } - - buffer = (u32 *)obj->buffer.pointer; - - if (buffer[0] != 0x4C4C4544 && buffer[1] != 0x494D5720) - dev_warn(&wdev->dev, "Dell descriptor buffer has invalid signature (%*ph)\n", - 8, buffer); - - if (buffer[2] != 0 && buffer[2] != 1) - dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%d)\n", - buffer[2]); - - if (buffer[3] != 4096) - dev_warn(&wdev->dev, "Dell descriptor buffer has invalid buffer length (%d)\n", - buffer[3]); - - priv->interface_version = buffer[2]; - ret = 0; - - dev_info(&wdev->dev, "Detected Dell WMI interface version %u\n", - priv->interface_version); - -out: - kfree(obj); - put_device(&desc_dev->dev); - return ret; -} - -/* * According to Dell SMBIOS documentation: * * 17 3 Application Program Registration @@ -711,13 +638,16 @@ static int dell_wmi_events_set_enabled(bool enable) struct calling_interface_buffer *buffer; int ret; - buffer = dell_smbios_get_buffer(); + buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL); + buffer->cmd_class = CLASS_INFO; + buffer->cmd_select = SELECT_APP_REGISTRATION; buffer->input[0] = 0x10000; buffer->input[1] = 0x51534554; buffer->input[3] = enable; - dell_smbios_send_request(17, 3); - ret = buffer->output[0]; - dell_smbios_release_buffer(); + ret = dell_smbios_call(buffer); + if (ret == 0) + ret = buffer->output[0]; + kfree(buffer); return dell_smbios_error(ret); } @@ -725,7 +655,11 @@ static int dell_wmi_events_set_enabled(bool enable) static int dell_wmi_probe(struct wmi_device *wdev) { struct dell_wmi_priv *priv; - int err; + int ret; + + ret = dell_wmi_get_descriptor_valid(); + if (ret) + return ret; priv = devm_kzalloc( &wdev->dev, sizeof(struct dell_wmi_priv), GFP_KERNEL); @@ -733,9 +667,8 @@ static int dell_wmi_probe(struct wmi_device *wdev) return -ENOMEM; dev_set_drvdata(&wdev->dev, priv); - err = dell_wmi_check_descriptor_buffer(wdev); - if (err) - return err; + if (!dell_wmi_get_interface_version(&priv->interface_version)) + return -EPROBE_DEFER; return dell_wmi_input_setup(wdev); } diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 56a8195096a2..2cfbd3fa5136 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -691,6 +691,7 @@ static enum led_brightness eco_led_get(struct led_classdev *cdev) static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) { + struct fujitsu_laptop *priv = acpi_driver_data(device); struct led_classdev *led; int result; @@ -724,12 +725,15 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) } /* - * BTNI bit 24 seems to indicate the presence of a radio toggle - * button in place of a slide switch, and all such machines appear - * to also have an RF LED. Therefore use bit 24 as an indicator - * that an RF LED is present. + * Some Fujitsu laptops have a radio toggle button in place of a slide + * switch and all such machines appear to also have an RF LED. Based on + * comparing DSDT tables of four Fujitsu Lifebook models (E744, E751, + * S7110, S8420; the first one has a radio toggle button, the other + * three have slide switches), bit 17 of flags_supported (the value + * returned by method S000 of ACPI device FUJ02E3) seems to indicate + * whether given model has a radio toggle button. */ - if (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) { + if (priv->flags_supported & BIT(17)) { led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index b4ed3dc983d5..b4224389febe 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -297,7 +297,7 @@ static int hp_wmi_hw_state(int mask) if (state < 0) return state; - return state & 0x1; + return !!(state & mask); } static int __init hp_wmi_bios_2008_later(void) diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 493d8910a74e..7b12abe86b94 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -240,6 +240,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = { AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted), AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left), AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd), + AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted), AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left), AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index fe98d4ac0df3..53ab4e0f8962 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -1166,6 +1166,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 910-13IKB"), }, }, + { + .ident = "Lenovo YOGA 920-13IKB", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 920-13IKB"), + }, + }, {} }; diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index e34fd70b67af..f470279c4c10 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -226,6 +226,24 @@ wakeup: return; } + /* + * Needed for suspend to work on some platforms that don't expose + * the 5-button array, but still send notifies with power button + * event code to this device object on power button actions. + * + * Report the power button press; catch and ignore the button release. + */ + if (!priv->array) { + if (event == 0xce) { + input_report_key(priv->input_dev, KEY_POWER, 1); + input_sync(priv->input_dev); + return; + } + + if (event == 0xcf) + return; + } + /* 0xC0 is for HID events, other values are for 5 button array */ if (event != 0xc0) { if (!priv->array || diff --git a/drivers/platform/x86/intel-wmi-thunderbolt.c b/drivers/platform/x86/intel-wmi-thunderbolt.c new file mode 100644 index 000000000000..c2257bd06f18 --- /dev/null +++ b/drivers/platform/x86/intel-wmi-thunderbolt.c @@ -0,0 +1,98 @@ +/* + * WMI Thunderbolt driver + * + * Copyright (C) 2017 Dell Inc. All Rights Reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/wmi.h> + +#define INTEL_WMI_THUNDERBOLT_GUID "86CCFD48-205E-4A77-9C48-2021CBEDE341" + +static ssize_t force_power_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_buffer input; + acpi_status status; + u8 mode; + + input.length = sizeof(u8); + input.pointer = &mode; + mode = hex_to_bin(buf[0]); + if (mode == 0 || mode == 1) { + status = wmi_evaluate_method(INTEL_WMI_THUNDERBOLT_GUID, 0, 1, + &input, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + } else { + return -EINVAL; + } + return count; +} + +static DEVICE_ATTR_WO(force_power); + +static struct attribute *tbt_attrs[] = { + &dev_attr_force_power.attr, + NULL +}; + +static const struct attribute_group tbt_attribute_group = { + .attrs = tbt_attrs, +}; + +static int intel_wmi_thunderbolt_probe(struct wmi_device *wdev) +{ + int ret; + + ret = sysfs_create_group(&wdev->dev.kobj, &tbt_attribute_group); + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); + return ret; +} + +static int intel_wmi_thunderbolt_remove(struct wmi_device *wdev) +{ + sysfs_remove_group(&wdev->dev.kobj, &tbt_attribute_group); + kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE); + return 0; +} + +static const struct wmi_device_id intel_wmi_thunderbolt_id_table[] = { + { .guid_string = INTEL_WMI_THUNDERBOLT_GUID }, + { }, +}; + +static struct wmi_driver intel_wmi_thunderbolt_driver = { + .driver = { + .name = "intel-wmi-thunderbolt", + }, + .probe = intel_wmi_thunderbolt_probe, + .remove = intel_wmi_thunderbolt_remove, + .id_table = intel_wmi_thunderbolt_id_table, +}; + +module_wmi_driver(intel_wmi_thunderbolt_driver); + +MODULE_ALIAS("wmi:" INTEL_WMI_THUNDERBOLT_GUID); +MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>"); +MODULE_DESCRIPTION("Intel WMI Thunderbolt force power driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe.c index da706e2c4232..380ef7ec094f 100644 --- a/drivers/platform/x86/intel_cht_int33fe.c +++ b/drivers/platform/x86/intel_cht_int33fe.c @@ -24,6 +24,7 @@ #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #define EXPECTED_PTYPE 4 @@ -34,6 +35,42 @@ struct cht_int33fe_data { struct i2c_client *pi3usb30532; }; +/* + * Grrr I severly dislike buggy BIOS-es. At least one BIOS enumerates + * the max17047 both through the INT33FE ACPI device (it is right there + * in the resources table) as well as through a separate MAX17047 device. + * + * These helpers are used to work around this by checking if an i2c-client + * for the max17047 has already been registered. + */ +static int cht_int33fe_check_for_max17047(struct device *dev, void *data) +{ + struct i2c_client **max17047 = data; + struct acpi_device *adev; + const char *hid; + + adev = ACPI_COMPANION(dev); + if (!adev) + return 0; + + hid = acpi_device_hid(adev); + + /* The MAX17047 ACPI node doesn't have an UID, so we don't check that */ + if (strcmp(hid, "MAX17047")) + return 0; + + *max17047 = to_i2c_client(dev); + return 1; +} + +static struct i2c_client *cht_int33fe_find_max17047(void) +{ + struct i2c_client *max17047 = NULL; + + i2c_for_each_dev(&max17047, cht_int33fe_check_for_max17047); + return max17047; +} + static const char * const max17047_suppliers[] = { "bq24190-charger" }; static const struct property_entry max17047_props[] = { @@ -41,14 +78,25 @@ static const struct property_entry max17047_props[] = { { } }; +static const struct property_entry fusb302_props[] = { + PROPERTY_ENTRY_STRING("fcs,extcon-name", "cht_wcove_pwrsrc"), + PROPERTY_ENTRY_U32("fcs,max-sink-microvolt", 12000000), + PROPERTY_ENTRY_U32("fcs,max-sink-microamp", 3000000), + PROPERTY_ENTRY_U32("fcs,max-sink-microwatt", 36000000), + { } +}; + static int cht_int33fe_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct i2c_board_info board_info; struct cht_int33fe_data *data; + struct i2c_client *max17047; + struct regulator *regulator; unsigned long long ptyp; acpi_status status; int fusb302_irq; + int ret; status = acpi_evaluate_integer(ACPI_HANDLE(dev), "PTYP", NULL, &ptyp); if (ACPI_FAILURE(status)) { @@ -63,6 +111,34 @@ static int cht_int33fe_probe(struct i2c_client *client) if (ptyp != EXPECTED_PTYPE) return -ENODEV; + /* Check presence of INT34D3 (hardware-rev 3) expected for ptype == 4 */ + if (!acpi_dev_present("INT34D3", "1", 3)) { + dev_err(dev, "Error PTYPE == %d, but no INT34D3 device\n", + EXPECTED_PTYPE); + return -ENODEV; + } + + /* + * We expect the WC PMIC to be paired with a TI bq24292i charger-IC. + * We check for the bq24292i vbus regulator here, this has 2 purposes: + * 1) The bq24292i allows charging with up to 12V, setting the fusb302's + * max-snk voltage to 12V with another charger-IC is not good. + * 2) For the fusb302 driver to get the bq24292i vbus regulator, the + * regulator-map, which is part of the bq24292i regulator_init_data, + * must be registered before the fusb302 is instantiated, otherwise + * it will end up with a dummy-regulator. + * Note "cht_wc_usb_typec_vbus" comes from the regulator_init_data + * which is defined in i2c-cht-wc.c from where the bq24292i i2c-client + * gets instantiated. We use regulator_get_optional here so that we + * don't end up getting a dummy-regulator ourselves. + */ + regulator = regulator_get_optional(dev, "cht_wc_usb_typec_vbus"); + if (IS_ERR(regulator)) { + ret = PTR_ERR(regulator); + return (ret == -ENODEV) ? -EPROBE_DEFER : ret; + } + regulator_put(regulator); + /* The FUSB302 uses the irq at index 1 and is the only irq user */ fusb302_irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 1); if (fusb302_irq < 0) { @@ -75,16 +151,31 @@ static int cht_int33fe_probe(struct i2c_client *client) if (!data) return -ENOMEM; - memset(&board_info, 0, sizeof(board_info)); - strlcpy(board_info.type, "max17047", I2C_NAME_SIZE); - board_info.properties = max17047_props; - - data->max17047 = i2c_acpi_new_device(dev, 1, &board_info); - if (!data->max17047) - return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */ + /* Work around BIOS bug, see comment on cht_int33fe_find_max17047 */ + max17047 = cht_int33fe_find_max17047(); + if (max17047) { + /* Pre-existing i2c-client for the max17047, add device-props */ + ret = device_add_properties(&max17047->dev, max17047_props); + if (ret) + return ret; + /* And re-probe to get the new device-props applied. */ + ret = device_reprobe(&max17047->dev); + if (ret) + dev_warn(dev, "Reprobing max17047 error: %d\n", ret); + } else { + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, "max17047", I2C_NAME_SIZE); + board_info.dev_name = "max17047"; + board_info.properties = max17047_props; + data->max17047 = i2c_acpi_new_device(dev, 1, &board_info); + if (!data->max17047) + return -EPROBE_DEFER; /* Wait for i2c-adapter to load */ + } memset(&board_info, 0, sizeof(board_info)); - strlcpy(board_info.type, "fusb302", I2C_NAME_SIZE); + strlcpy(board_info.type, "typec_fusb302", I2C_NAME_SIZE); + board_info.dev_name = "fusb302"; + board_info.properties = fusb302_props; board_info.irq = fusb302_irq; data->fusb302 = i2c_acpi_new_device(dev, 2, &board_info); @@ -92,6 +183,7 @@ static int cht_int33fe_probe(struct i2c_client *client) goto out_unregister_max17047; memset(&board_info, 0, sizeof(board_info)); + board_info.dev_name = "pi3usb30532"; strlcpy(board_info.type, "pi3usb30532", I2C_NAME_SIZE); data->pi3usb30532 = i2c_acpi_new_device(dev, 3, &board_info); @@ -106,7 +198,8 @@ out_unregister_fusb302: i2c_unregister_device(data->fusb302); out_unregister_max17047: - i2c_unregister_device(data->max17047); + if (data->max17047) + i2c_unregister_device(data->max17047); return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */ } @@ -117,7 +210,8 @@ static int cht_int33fe_remove(struct i2c_client *i2c) i2c_unregister_device(data->pi3usb30532); i2c_unregister_device(data->fusb302); - i2c_unregister_device(data->max17047); + if (data->max17047) + i2c_unregister_device(data->max17047); return 0; } diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index 58dcee562d64..a0c95853fd3f 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -10,10 +10,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * The full GNU General Public License is included in this distribution in * the file called "COPYING". * @@ -259,8 +255,6 @@ static const int IPS_SAMPLE_WINDOW = 5000; /* 5s moving window of samples */ /* Per-SKU limits */ struct ips_mcp_limits { - int cpu_family; - int cpu_model; /* includes extended model... */ int mcp_power_limit; /* mW units */ int core_power_limit; int mch_power_limit; @@ -295,11 +289,14 @@ static struct ips_mcp_limits ips_ulv_limits = { }; struct ips_driver { - struct pci_dev *dev; - void *regmap; + struct device *dev; + void __iomem *regmap; + int irq; + struct task_struct *monitor; struct task_struct *adjust; struct dentry *debug_root; + struct timer_list timer; /* Average CPU core temps (all averages in .01 degrees C for precision) */ u16 ctv1_avg_temp; @@ -594,7 +591,7 @@ static void ips_disable_gpu_turbo(struct ips_driver *ips) return; if (!ips->gpu_turbo_disable()) - dev_err(&ips->dev->dev, "failed to disable graphics turbo\n"); + dev_err(ips->dev, "failed to disable graphics turbo\n"); else ips->__gpu_turbo_on = false; } @@ -649,8 +646,7 @@ static bool cpu_exceeded(struct ips_driver *ips, int cpu) spin_unlock_irqrestore(&ips->turbo_status_lock, flags); if (ret) - dev_info(&ips->dev->dev, - "CPU power or thermal limit exceeded\n"); + dev_info(ips->dev, "CPU power or thermal limit exceeded\n"); return ret; } @@ -769,7 +765,7 @@ static int ips_adjust(void *data) struct ips_driver *ips = data; unsigned long flags; - dev_dbg(&ips->dev->dev, "starting ips-adjust thread\n"); + dev_dbg(ips->dev, "starting ips-adjust thread\n"); /* * Adjust CPU and GPU clamps every 5s if needed. Doing it more @@ -816,7 +812,7 @@ sleep: schedule_timeout_interruptible(msecs_to_jiffies(IPS_ADJUST_PERIOD)); } while (!kthread_should_stop()); - dev_dbg(&ips->dev->dev, "ips-adjust thread stopped\n"); + dev_dbg(ips->dev, "ips-adjust thread stopped\n"); return 0; } @@ -942,9 +938,10 @@ static u32 calc_avg_power(struct ips_driver *ips, u32 *array) return avg; } -static void monitor_timeout(unsigned long arg) +static void monitor_timeout(struct timer_list *t) { - wake_up_process((struct task_struct *)arg); + struct ips_driver *ips = from_timer(ips, t, timer); + wake_up_process(ips->monitor); } /** @@ -961,7 +958,6 @@ static void monitor_timeout(unsigned long arg) static int ips_monitor(void *data) { struct ips_driver *ips = data; - struct timer_list timer; unsigned long seqno_timestamp, expire, last_msecs, last_sample_period; int i; u32 *cpu_samples, *mchp_samples, old_cpu_power; @@ -976,7 +972,7 @@ static int ips_monitor(void *data) mchp_samples = kzalloc(sizeof(u32) * IPS_SAMPLE_COUNT, GFP_KERNEL); if (!mcp_samples || !ctv1_samples || !ctv2_samples || !mch_samples || !cpu_samples || !mchp_samples) { - dev_err(&ips->dev->dev, + dev_err(ips->dev, "failed to allocate sample array, ips disabled\n"); kfree(mcp_samples); kfree(ctv1_samples); @@ -1049,8 +1045,7 @@ static int ips_monitor(void *data) schedule_timeout_interruptible(msecs_to_jiffies(IPS_SAMPLE_PERIOD)); last_sample_period = IPS_SAMPLE_PERIOD; - setup_deferrable_timer_on_stack(&timer, monitor_timeout, - (unsigned long)current); + timer_setup(&ips->timer, monitor_timeout, TIMER_DEFERRABLE); do { u32 cpu_val, mch_val; u16 val; @@ -1097,7 +1092,8 @@ static int ips_monitor(void *data) ITV_ME_SEQNO_SHIFT; if (cur_seqno == last_seqno && time_after(jiffies, seqno_timestamp + HZ)) { - dev_warn(&ips->dev->dev, "ME failed to update for more than 1s, likely hung\n"); + dev_warn(ips->dev, + "ME failed to update for more than 1s, likely hung\n"); } else { seqno_timestamp = get_jiffies_64(); last_seqno = cur_seqno; @@ -1107,7 +1103,7 @@ static int ips_monitor(void *data) expire = jiffies + msecs_to_jiffies(IPS_SAMPLE_PERIOD); __set_current_state(TASK_INTERRUPTIBLE); - mod_timer(&timer, expire); + mod_timer(&ips->timer, expire); schedule(); /* Calculate actual sample period for power averaging */ @@ -1116,10 +1112,9 @@ static int ips_monitor(void *data) last_sample_period = 1; } while (!kthread_should_stop()); - del_timer_sync(&timer); - destroy_timer_on_stack(&timer); + del_timer_sync(&ips->timer); - dev_dbg(&ips->dev->dev, "ips-monitor thread stopped\n"); + dev_dbg(ips->dev, "ips-monitor thread stopped\n"); return 0; } @@ -1128,17 +1123,17 @@ static int ips_monitor(void *data) #define THM_DUMPW(reg) \ { \ u16 val = thm_readw(reg); \ - dev_dbg(&ips->dev->dev, #reg ": 0x%04x\n", val); \ + dev_dbg(ips->dev, #reg ": 0x%04x\n", val); \ } #define THM_DUMPL(reg) \ { \ u32 val = thm_readl(reg); \ - dev_dbg(&ips->dev->dev, #reg ": 0x%08x\n", val); \ + dev_dbg(ips->dev, #reg ": 0x%08x\n", val); \ } #define THM_DUMPQ(reg) \ { \ u64 val = thm_readq(reg); \ - dev_dbg(&ips->dev->dev, #reg ": 0x%016x\n", val); \ + dev_dbg(ips->dev, #reg ": 0x%016x\n", val); \ } static void dump_thermal_info(struct ips_driver *ips) @@ -1146,7 +1141,7 @@ static void dump_thermal_info(struct ips_driver *ips) u16 ptl; ptl = thm_readw(THM_PTL); - dev_dbg(&ips->dev->dev, "Processor temp limit: %d\n", ptl); + dev_dbg(ips->dev, "Processor temp limit: %d\n", ptl); THM_DUMPW(THM_CTA); THM_DUMPW(THM_TRC); @@ -1175,8 +1170,8 @@ static irqreturn_t ips_irq_handler(int irq, void *arg) if (!tses && !tes) return IRQ_NONE; - dev_info(&ips->dev->dev, "TSES: 0x%02x\n", tses); - dev_info(&ips->dev->dev, "TES: 0x%02x\n", tes); + dev_info(ips->dev, "TSES: 0x%02x\n", tses); + dev_info(ips->dev, "TES: 0x%02x\n", tes); /* STS update from EC? */ if (tes & 1) { @@ -1214,8 +1209,8 @@ static irqreturn_t ips_irq_handler(int irq, void *arg) /* Thermal trip */ if (tses) { - dev_warn(&ips->dev->dev, - "thermal trip occurred, tses: 0x%04x\n", tses); + dev_warn(ips->dev, "thermal trip occurred, tses: 0x%04x\n", + tses); thm_writeb(THM_TSES, tses); } @@ -1330,8 +1325,7 @@ static void ips_debugfs_init(struct ips_driver *ips) ips->debug_root = debugfs_create_dir("ips", NULL); if (!ips->debug_root) { - dev_err(&ips->dev->dev, - "failed to create debugfs entries: %ld\n", + dev_err(ips->dev, "failed to create debugfs entries: %ld\n", PTR_ERR(ips->debug_root)); return; } @@ -1345,8 +1339,7 @@ static void ips_debugfs_init(struct ips_driver *ips) ips->debug_root, node, &ips_debugfs_ops); if (!ent) { - dev_err(&ips->dev->dev, - "failed to create debug file: %ld\n", + dev_err(ips->dev, "failed to create debug file: %ld\n", PTR_ERR(ent)); goto err_cleanup; } @@ -1373,8 +1366,8 @@ static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips) u16 tdp; if (!(boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 37)) { - dev_info(&ips->dev->dev, "Non-IPS CPU detected.\n"); - goto out; + dev_info(ips->dev, "Non-IPS CPU detected.\n"); + return NULL; } rdmsrl(IA32_MISC_ENABLE, misc_en); @@ -1395,8 +1388,8 @@ static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips) else if (strstr(boot_cpu_data.x86_model_id, "CPU U")) limits = &ips_ulv_limits; else { - dev_info(&ips->dev->dev, "No CPUID match found.\n"); - goto out; + dev_info(ips->dev, "No CPUID match found.\n"); + return NULL; } rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_power); @@ -1404,12 +1397,12 @@ static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips) /* Sanity check TDP against CPU */ if (limits->core_power_limit != (tdp / 8) * 1000) { - dev_info(&ips->dev->dev, "CPU TDP doesn't match expected value (found %d, expected %d)\n", + dev_info(ips->dev, + "CPU TDP doesn't match expected value (found %d, expected %d)\n", tdp / 8, limits->core_power_limit / 1000); limits->core_power_limit = (tdp / 8) * 1000; } -out: return limits; } @@ -1459,7 +1452,7 @@ ips_gpu_turbo_enabled(struct ips_driver *ips) { if (!ips->gpu_busy && late_i915_load) { if (ips_get_i915_syms(ips)) { - dev_info(&ips->dev->dev, + dev_info(ips->dev, "i915 driver attached, reenabling gpu turbo\n"); ips->gpu_turbo_enabled = !(thm_readl(THM_HTS) & HTS_GTD_DIS); } @@ -1480,8 +1473,7 @@ ips_link_to_i915_driver(void) EXPORT_SYMBOL_GPL(ips_link_to_i915_driver); static const struct pci_device_id ips_id_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_THERMAL_SENSOR), }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_THERMAL_SENSOR), }, { 0, } }; @@ -1517,62 +1509,45 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id) if (dmi_check_system(ips_blacklist)) return -ENODEV; - ips = kzalloc(sizeof(struct ips_driver), GFP_KERNEL); + ips = devm_kzalloc(&dev->dev, sizeof(*ips), GFP_KERNEL); if (!ips) return -ENOMEM; - pci_set_drvdata(dev, ips); - ips->dev = dev; + spin_lock_init(&ips->turbo_status_lock); + ips->dev = &dev->dev; ips->limits = ips_detect_cpu(ips); if (!ips->limits) { dev_info(&dev->dev, "IPS not supported on this CPU\n"); - ret = -ENXIO; - goto error_free; + return -ENXIO; } - spin_lock_init(&ips->turbo_status_lock); - - ret = pci_enable_device(dev); + ret = pcim_enable_device(dev); if (ret) { dev_err(&dev->dev, "can't enable PCI device, aborting\n"); - goto error_free; + return ret; } - if (!pci_resource_start(dev, 0)) { - dev_err(&dev->dev, "TBAR not assigned, aborting\n"); - ret = -ENXIO; - goto error_free; - } - - ret = pci_request_regions(dev, "ips thermal sensor"); + ret = pcim_iomap_regions(dev, 1 << 0, pci_name(dev)); if (ret) { - dev_err(&dev->dev, "thermal resource busy, aborting\n"); - goto error_free; - } - - - ips->regmap = ioremap(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); - if (!ips->regmap) { dev_err(&dev->dev, "failed to map thermal regs, aborting\n"); - ret = -EBUSY; - goto error_release; + return ret; } + ips->regmap = pcim_iomap_table(dev)[0]; + + pci_set_drvdata(dev, ips); tse = thm_readb(THM_TSE); if (tse != TSE_EN) { dev_err(&dev->dev, "thermal device not enabled (0x%02x), aborting\n", tse); - ret = -ENXIO; - goto error_unmap; + return -ENXIO; } trc = thm_readw(THM_TRC); trc_required_mask = TRC_CORE1_EN | TRC_CORE_PWR | TRC_MCH_EN; if ((trc & trc_required_mask) != trc_required_mask) { dev_err(&dev->dev, "thermal reporting for required devices not enabled, aborting\n"); - ret = -ENXIO; - goto error_unmap; + return -ENXIO; } if (trc & TRC_CORE2_EN) @@ -1602,20 +1577,23 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id) rdmsrl(PLATFORM_INFO, platform_info); if (!(platform_info & PLATFORM_TDP)) { dev_err(&dev->dev, "platform indicates TDP override unavailable, aborting\n"); - ret = -ENODEV; - goto error_unmap; + return -ENODEV; } /* * IRQ handler for ME interaction * Note: don't use MSI here as the PCH has bugs. */ - pci_disable_msi(dev); - ret = request_irq(dev->irq, ips_irq_handler, IRQF_SHARED, "ips", - ips); + ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY); + if (ret < 0) + return ret; + + ips->irq = pci_irq_vector(dev, 0); + + ret = request_irq(ips->irq, ips_irq_handler, IRQF_SHARED, "ips", ips); if (ret) { dev_err(&dev->dev, "request irq failed, aborting\n"); - goto error_unmap; + return ret; } /* Enable aux, hot & critical interrupts */ @@ -1672,13 +1650,8 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id) error_thread_cleanup: kthread_stop(ips->adjust); error_free_irq: - free_irq(ips->dev->irq, ips); -error_unmap: - iounmap(ips->regmap); -error_release: - pci_release_regions(dev); -error_free: - kfree(ips); + free_irq(ips->irq, ips); + pci_free_irq_vectors(dev); return ret; } @@ -1709,27 +1682,20 @@ static void ips_remove(struct pci_dev *dev) wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override); wrmsrl(TURBO_POWER_CURRENT_LIMIT, ips->orig_turbo_limit); - free_irq(ips->dev->irq, ips); + free_irq(ips->irq, ips); + pci_free_irq_vectors(dev); if (ips->adjust) kthread_stop(ips->adjust); if (ips->monitor) kthread_stop(ips->monitor); - iounmap(ips->regmap); - pci_release_regions(dev); - kfree(ips); dev_dbg(&dev->dev, "IPS driver removed\n"); } -static void ips_shutdown(struct pci_dev *dev) -{ -} - static struct pci_driver ips_pci_driver = { .name = "intel ips", .id_table = ips_id_table, .probe = ips_probe, .remove = ips_remove, - .shutdown = ips_shutdown, }; module_pci_driver(ips_pci_driver); diff --git a/drivers/platform/x86/intel_ips.h b/drivers/platform/x86/intel_ips.h index 73299beff5b3..60f4e3ddbe9f 100644 --- a/drivers/platform/x86/intel_ips.h +++ b/drivers/platform/x86/intel_ips.h @@ -10,10 +10,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * * The full GNU General Public License is included in this distribution in * the file called "COPYING". */ diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c index a47a41fc10ad..b5b890127479 100644 --- a/drivers/platform/x86/intel_punit_ipc.c +++ b/drivers/platform/x86/intel_punit_ipc.c @@ -252,28 +252,28 @@ static int intel_punit_get_bars(struct platform_device *pdev) * - GTDRIVER_IPC BASE_IFACE */ res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - if (res) { + if (res && resource_size(res) > 1) { addr = devm_ioremap_resource(&pdev->dev, res); if (!IS_ERR(addr)) punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr; } res = platform_get_resource(pdev, IORESOURCE_MEM, 3); - if (res) { + if (res && resource_size(res) > 1) { addr = devm_ioremap_resource(&pdev->dev, res); if (!IS_ERR(addr)) punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr; } res = platform_get_resource(pdev, IORESOURCE_MEM, 4); - if (res) { + if (res && resource_size(res) > 1) { addr = devm_ioremap_resource(&pdev->dev, res); if (!IS_ERR(addr)) punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr; } res = platform_get_resource(pdev, IORESOURCE_MEM, 5); - if (res) { + if (res && resource_size(res) > 1) { addr = devm_ioremap_resource(&pdev->dev, res); if (!IS_ERR(addr)) punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr; diff --git a/drivers/platform/x86/intel_telemetry_core.c b/drivers/platform/x86/intel_telemetry_core.c index 0d4c3808a6d8..f378621b5fe9 100644 --- a/drivers/platform/x86/intel_telemetry_core.c +++ b/drivers/platform/x86/intel_telemetry_core.c @@ -15,9 +15,8 @@ * Telemetry Framework provides platform related PM and performance statistics. * This file provides the core telemetry API implementation. */ -#include <linux/module.h> -#include <linux/init.h> #include <linux/device.h> +#include <linux/module.h> #include <asm/intel_telemetry.h> diff --git a/drivers/platform/x86/intel_telemetry_debugfs.c b/drivers/platform/x86/intel_telemetry_debugfs.c index d4fc42b4cbeb..4249e8267bbc 100644 --- a/drivers/platform/x86/intel_telemetry_debugfs.c +++ b/drivers/platform/x86/intel_telemetry_debugfs.c @@ -21,14 +21,12 @@ * /sys/kernel/debug/telemetry/ioss_race_verbosity: Write and Change Tracing * Verbosity via firmware */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/device.h> #include <linux/debugfs.h> -#include <linux/seq_file.h> +#include <linux/device.h> #include <linux/io.h> -#include <linux/uaccess.h> +#include <linux/module.h> #include <linux/pci.h> +#include <linux/seq_file.h> #include <linux/suspend.h> #include <asm/cpu_device_id.h> @@ -76,8 +74,6 @@ #define TELEM_IOSS_DX_D0IX_EVTS 25 #define TELEM_IOSS_PG_EVTS 30 -#define TELEM_EVT_LEN(x) (sizeof(x)/sizeof((x)[0])) - #define TELEM_DEBUGFS_CPU(model, data) \ { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data} @@ -304,13 +300,13 @@ static struct telemetry_debugfs_conf telem_apl_debugfs_conf = { .ioss_d0ix_data = telem_apl_ioss_d0ix_data, .ioss_pg_data = telem_apl_ioss_pg_data, - .pss_idle_evts = TELEM_EVT_LEN(telem_apl_pss_idle_data), - .pcs_idle_blkd_evts = TELEM_EVT_LEN(telem_apl_pcs_idle_blkd_data), - .pcs_s0ix_blkd_evts = TELEM_EVT_LEN(telem_apl_pcs_s0ix_blkd_data), - .pss_ltr_evts = TELEM_EVT_LEN(telem_apl_pss_ltr_data), - .pss_wakeup_evts = TELEM_EVT_LEN(telem_apl_pss_wakeup), - .ioss_d0ix_evts = TELEM_EVT_LEN(telem_apl_ioss_d0ix_data), - .ioss_pg_evts = TELEM_EVT_LEN(telem_apl_ioss_pg_data), + .pss_idle_evts = ARRAY_SIZE(telem_apl_pss_idle_data), + .pcs_idle_blkd_evts = ARRAY_SIZE(telem_apl_pcs_idle_blkd_data), + .pcs_s0ix_blkd_evts = ARRAY_SIZE(telem_apl_pcs_s0ix_blkd_data), + .pss_ltr_evts = ARRAY_SIZE(telem_apl_pss_ltr_data), + .pss_wakeup_evts = ARRAY_SIZE(telem_apl_pss_wakeup), + .ioss_d0ix_evts = ARRAY_SIZE(telem_apl_ioss_d0ix_data), + .ioss_pg_evts = ARRAY_SIZE(telem_apl_ioss_pg_data), .pstates_id = TELEM_APL_PSS_PSTATES_ID, .pss_idle_id = TELEM_APL_PSS_IDLE_ID, diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c index e0424d5a795a..2f889d6c270e 100644 --- a/drivers/platform/x86/intel_telemetry_pltdrv.c +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c @@ -16,15 +16,9 @@ * It used the PUNIT and PMC IPC interfaces for configuring the counters. * The accumulated results are fetched from SRAM. */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/device.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> + #include <linux/io.h> -#include <linux/uaccess.h> -#include <linux/pci.h> -#include <linux/suspend.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <asm/cpu_device_id.h> @@ -256,7 +250,7 @@ static int telemetry_check_evtid(enum telemetry_unit telem_unit, break; default: - pr_err("Unknown Telemetry action Specified %d\n", action); + pr_err("Unknown Telemetry action specified %d\n", action); return -EINVAL; } @@ -659,7 +653,7 @@ static int telemetry_setup(struct platform_device *pdev) ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, TELEM_RESET); if (ret) { - dev_err(&pdev->dev, "TELEMTRY Setup Failed\n"); + dev_err(&pdev->dev, "TELEMETRY Setup Failed\n"); return ret; } return 0; @@ -685,7 +679,7 @@ static int telemetry_plt_update_events(struct telemetry_evtconfig pss_evtconfig, ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, TELEM_UPDATE); if (ret) - pr_err("TELEMTRY Config Failed\n"); + pr_err("TELEMETRY Config Failed\n"); return ret; } @@ -822,7 +816,7 @@ static int telemetry_plt_reset_events(void) ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, TELEM_RESET); if (ret) - pr_err("TELEMTRY Reset Failed\n"); + pr_err("TELEMETRY Reset Failed\n"); return ret; } @@ -885,7 +879,7 @@ static int telemetry_plt_add_events(u8 num_pss_evts, u8 num_ioss_evts, ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig, TELEM_ADD); if (ret) - pr_err("TELEMTRY ADD Failed\n"); + pr_err("TELEMETRY ADD Failed\n"); return ret; } @@ -1195,7 +1189,7 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev) ret = telemetry_set_pltdata(&telm_pltops, telm_conf); if (ret) { - dev_err(&pdev->dev, "TELEMTRY Set Pltops Failed.\n"); + dev_err(&pdev->dev, "TELEMETRY Set Pltops Failed.\n"); goto out; } @@ -1210,7 +1204,7 @@ out: iounmap(telm_conf->pss_config.regmap); if (telm_conf->ioss_config.regmap) iounmap(telm_conf->ioss_config.regmap); - dev_err(&pdev->dev, "TELEMTRY Setup Failed.\n"); + dev_err(&pdev->dev, "TELEMETRY Setup Failed.\n"); return ret; } @@ -1234,7 +1228,6 @@ static struct platform_driver telemetry_soc_driver = { static int __init telemetry_module_init(void) { - pr_info(DRIVER_NAME ": version %s loaded\n", DRIVER_VERSION); return platform_driver_register(&telemetry_soc_driver); } diff --git a/drivers/platform/x86/intel_turbo_max_3.c b/drivers/platform/x86/intel_turbo_max_3.c index 4f60d8e32a0a..d4ea01805879 100644 --- a/drivers/platform/x86/intel_turbo_max_3.c +++ b/drivers/platform/x86/intel_turbo_max_3.c @@ -125,6 +125,7 @@ static int itmt_legacy_cpu_online(unsigned int cpu) static const struct x86_cpu_id itmt_legacy_cpu_ids[] = { ICPU(INTEL_FAM6_BROADWELL_X), + ICPU(INTEL_FAM6_SKYLAKE_X), {} }; diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 4f3de2a8c4df..504256c3660d 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -216,8 +216,8 @@ static struct resource mlxplat_mlxcpld_resources[] = { [0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"), }; -struct platform_device *mlxplat_dev; -struct mlxcpld_hotplug_platform_data *mlxplat_hotplug; +static struct platform_device *mlxplat_dev; +static struct mlxcpld_hotplug_platform_data *mlxplat_hotplug; static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) { diff --git a/drivers/platform/x86/peaq-wmi.c b/drivers/platform/x86/peaq-wmi.c index bc98ef95514a..9b9e1f39bbfb 100644 --- a/drivers/platform/x86/peaq-wmi.c +++ b/drivers/platform/x86/peaq-wmi.c @@ -8,6 +8,7 @@ */ #include <linux/acpi.h> +#include <linux/dmi.h> #include <linux/input-polldev.h> #include <linux/kernel.h> #include <linux/module.h> @@ -64,8 +65,23 @@ static void peaq_wmi_poll(struct input_polled_dev *dev) } } +/* Some other devices (Shuttle XS35) use the same WMI GUID for other purposes */ +static const struct dmi_system_id peaq_dmi_table[] __initconst = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), + DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), + }, + }, + {} +}; + static int __init peaq_wmi_init(void) { + /* WMI GUID is not unique, also check for a DMI match */ + if (!dmi_check_system(peaq_dmi_table)) + return -ENODEV; + if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID)) return -ENODEV; @@ -86,9 +102,6 @@ static int __init peaq_wmi_init(void) static void __exit peaq_wmi_exit(void) { - if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID)) - return; - input_unregister_polled_device(peaq_poll_dev); } diff --git a/drivers/platform/x86/silead_dmi.c b/drivers/platform/x86/silead_dmi.c index 1157a7b646d6..266535c2a72f 100644 --- a/drivers/platform/x86/silead_dmi.c +++ b/drivers/platform/x86/silead_dmi.c @@ -58,6 +58,7 @@ static const struct property_entry dexp_ursus_7w_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-y", 630), PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-dexp-ursus-7w.fw"), PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -72,6 +73,7 @@ static const struct property_entry surftab_wintron70_st70416_6_props[] = { PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-surftab-wintron70-st70416-6.fw"), PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -83,6 +85,8 @@ static const struct silead_ts_dmi_data surftab_wintron70_st70416_6_data = { static const struct property_entry gp_electronic_t701_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 960), PROPERTY_ENTRY_U32("touchscreen-size-y", 640), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-gp-electronic-t701.fw"), { } @@ -114,6 +118,7 @@ static const struct property_entry pov_mobii_wintab_p800w_props[] = { PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-pov-mobii-wintab-p800w.fw"), + PROPERTY_ENTRY_BOOL("silead,home-button"), { } }; @@ -136,6 +141,36 @@ static const struct silead_ts_dmi_data itworks_tw891_data = { .properties = itworks_tw891_props, }; +static const struct property_entry chuwi_hi8_pro_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1728), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1148), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-chuwi-hi8-pro.fw"), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct silead_ts_dmi_data chuwi_hi8_pro_data = { + .acpi_name = "MSSL1680:00", + .properties = chuwi_hi8_pro_props, +}; + +static const struct property_entry digma_citi_e200_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1980), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + PROPERTY_ENTRY_STRING("firmware-name", + "gsl1686-digma_citi_e200.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct silead_ts_dmi_data digma_citi_e200_data = { + .acpi_name = "MSSL1680:00", + .properties = digma_citi_e200_props, +}; + static const struct dmi_system_id silead_ts_dmi_table[] = { { /* CUBE iwork8 Air */ @@ -219,6 +254,23 @@ static const struct dmi_system_id silead_ts_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "TW891"), }, }, + { + /* Chuwi Hi8 Pro */ + .driver_data = (void *)&chuwi_hi8_pro_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"), + DMI_MATCH(DMI_PRODUCT_NAME, "X1D3_C806N"), + }, + }, + { + /* Digma Citi E200 */ + .driver_data = (void *)&digma_citi_e200_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Digma"), + DMI_MATCH(DMI_PRODUCT_NAME, "CITI E200"), + DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), + }, + }, { }, }; diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index a16cea2be9c3..62aa2c37b8d2 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1627,7 +1627,7 @@ static const struct rfkill_ops sony_rfkill_ops = { static int sony_nc_setup_rfkill(struct acpi_device *device, enum sony_nc_rfkill nc_type) { - int err = 0; + int err; struct rfkill *rfk; enum rfkill_type type; const char *name; @@ -1660,17 +1660,19 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, if (!rfk) return -ENOMEM; - if (sony_call_snc_handle(sony_rfkill_handle, 0x200, &result) < 0) { + err = sony_call_snc_handle(sony_rfkill_handle, 0x200, &result); + if (err < 0) { rfkill_destroy(rfk); - return -1; + return err; } hwblock = !(result & 0x1); - if (sony_call_snc_handle(sony_rfkill_handle, - sony_rfkill_address[nc_type], - &result) < 0) { + err = sony_call_snc_handle(sony_rfkill_handle, + sony_rfkill_address[nc_type], + &result); + if (err < 0) { rfkill_destroy(rfk); - return -1; + return err; } swblock = !(result & 0x2); diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 3887dfeafc96..117be48ff4de 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -310,8 +310,7 @@ static struct { enum { TP_HOTKEY_TABLET_NONE = 0, TP_HOTKEY_TABLET_USES_MHKG, - /* X1 Yoga 2016, seen on BIOS N1FET44W */ - TP_HOTKEY_TABLET_USES_CMMD, + TP_HOTKEY_TABLET_USES_GMMS, } hotkey_tablet; u32 kbdlight:1; u32 light:1; @@ -2044,8 +2043,28 @@ static void hotkey_poll_setup(const bool may_warn); /* HKEY.MHKG() return bits */ #define TP_HOTKEY_TABLET_MASK (1 << 3) -/* ThinkPad X1 Yoga (2016) */ -#define TP_EC_CMMD_TABLET_MODE 0x6 +enum { + TP_ACPI_MULTI_MODE_INVALID = 0, + TP_ACPI_MULTI_MODE_UNKNOWN = 1 << 0, + TP_ACPI_MULTI_MODE_LAPTOP = 1 << 1, + TP_ACPI_MULTI_MODE_TABLET = 1 << 2, + TP_ACPI_MULTI_MODE_FLAT = 1 << 3, + TP_ACPI_MULTI_MODE_STAND = 1 << 4, + TP_ACPI_MULTI_MODE_TENT = 1 << 5, + TP_ACPI_MULTI_MODE_STAND_TENT = 1 << 6, +}; + +enum { + /* The following modes are considered tablet mode for the purpose of + * reporting the status to userspace. i.e. in all these modes it makes + * sense to disable the laptop input devices such as touchpad and + * keyboard. + */ + TP_ACPI_MULTI_MODE_TABLET_LIKE = TP_ACPI_MULTI_MODE_TABLET | + TP_ACPI_MULTI_MODE_STAND | + TP_ACPI_MULTI_MODE_TENT | + TP_ACPI_MULTI_MODE_STAND_TENT, +}; static int hotkey_get_wlsw(void) { @@ -2066,6 +2085,90 @@ static int hotkey_get_wlsw(void) return (status) ? TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF; } +static int hotkey_gmms_get_tablet_mode(int s, int *has_tablet_mode) +{ + int type = (s >> 16) & 0xffff; + int value = s & 0xffff; + int mode = TP_ACPI_MULTI_MODE_INVALID; + int valid_modes = 0; + + if (has_tablet_mode) + *has_tablet_mode = 0; + + switch (type) { + case 1: + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | + TP_ACPI_MULTI_MODE_TABLET | + TP_ACPI_MULTI_MODE_STAND_TENT; + break; + case 2: + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | + TP_ACPI_MULTI_MODE_FLAT | + TP_ACPI_MULTI_MODE_TABLET | + TP_ACPI_MULTI_MODE_STAND | + TP_ACPI_MULTI_MODE_TENT; + break; + case 3: + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | + TP_ACPI_MULTI_MODE_FLAT; + break; + case 4: + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | + TP_ACPI_MULTI_MODE_TABLET | + TP_ACPI_MULTI_MODE_STAND | + TP_ACPI_MULTI_MODE_TENT; + break; + case 5: + valid_modes = TP_ACPI_MULTI_MODE_LAPTOP | + TP_ACPI_MULTI_MODE_FLAT | + TP_ACPI_MULTI_MODE_TABLET | + TP_ACPI_MULTI_MODE_STAND | + TP_ACPI_MULTI_MODE_TENT; + break; + default: + pr_err("Unknown multi mode status type %d with value 0x%04X, please report this to %s\n", + type, value, TPACPI_MAIL); + return 0; + } + + if (has_tablet_mode && (valid_modes & TP_ACPI_MULTI_MODE_TABLET_LIKE)) + *has_tablet_mode = 1; + + switch (value) { + case 1: + mode = TP_ACPI_MULTI_MODE_LAPTOP; + break; + case 2: + mode = TP_ACPI_MULTI_MODE_FLAT; + break; + case 3: + mode = TP_ACPI_MULTI_MODE_TABLET; + break; + case 4: + if (type == 1) + mode = TP_ACPI_MULTI_MODE_STAND_TENT; + else + mode = TP_ACPI_MULTI_MODE_STAND; + break; + case 5: + mode = TP_ACPI_MULTI_MODE_TENT; + break; + default: + if (type == 5 && value == 0xffff) { + pr_warn("Multi mode status is undetected, assuming laptop\n"); + return 0; + } + } + + if (!(mode & valid_modes)) { + pr_err("Unknown/reserved multi mode value 0x%04X for type %d, please report this to %s\n", + value, type, TPACPI_MAIL); + return 0; + } + + return !!(mode & TP_ACPI_MULTI_MODE_TABLET_LIKE); +} + static int hotkey_get_tablet_mode(int *status) { int s; @@ -2077,11 +2180,11 @@ static int hotkey_get_tablet_mode(int *status) *status = ((s & TP_HOTKEY_TABLET_MASK) != 0); break; - case TP_HOTKEY_TABLET_USES_CMMD: - if (!acpi_evalf(ec_handle, &s, "CMMD", "d")) + case TP_HOTKEY_TABLET_USES_GMMS: + if (!acpi_evalf(hkey_handle, &s, "GMMS", "dd", 0)) return -EIO; - *status = (s == TP_EC_CMMD_TABLET_MODE); + *status = hotkey_gmms_get_tablet_mode(s, NULL); break; default: break; @@ -3113,16 +3216,19 @@ static int hotkey_init_tablet_mode(void) int in_tablet_mode = 0, res; char *type = NULL; - if (acpi_evalf(hkey_handle, &res, "MHKG", "qd")) { + if (acpi_evalf(hkey_handle, &res, "GMMS", "qdd", 0)) { + int has_tablet_mode; + + in_tablet_mode = hotkey_gmms_get_tablet_mode(res, + &has_tablet_mode); + if (has_tablet_mode) + tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_GMMS; + type = "GMMS"; + } else if (acpi_evalf(hkey_handle, &res, "MHKG", "qd")) { /* For X41t, X60t, X61t Tablets... */ tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_MHKG; in_tablet_mode = !!(res & TP_HOTKEY_TABLET_MASK); type = "MHKG"; - } else if (acpi_evalf(ec_handle, &res, "CMMD", "qd")) { - /* For X1 Yoga (2016) */ - tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_CMMD; - in_tablet_mode = res == TP_EC_CMMD_TABLET_MODE; - type = "CMMD"; } if (!tp_features.hotkey_tablet) diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 0765b1797d4c..791449a2370f 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -33,17 +33,20 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/types.h> +#include <linux/acpi.h> #include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> #include <linux/list.h> -#include <linux/acpi.h> -#include <linux/slab.h> +#include <linux/miscdevice.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/wmi.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/uaccess.h> #include <linux/uuid.h> +#include <linux/wmi.h> +#include <uapi/linux/wmi.h> ACPI_MODULE_NAME("wmi"); MODULE_AUTHOR("Carlos Corbacho"); @@ -69,9 +72,12 @@ struct wmi_block { struct wmi_device dev; struct list_head list; struct guid_block gblock; + struct miscdevice char_dev; + struct mutex char_mutex; struct acpi_device *acpi_device; wmi_notify_handler handler; void *handler_data; + u64 req_buf_size; bool read_takes_no_args; }; @@ -188,6 +194,25 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) /* * Exported WMI functions */ + +/** + * set_required_buffer_size - Sets the buffer size needed for performing IOCTL + * @wdev: A wmi bus device from a driver + * @instance: Instance index + * + * Allocates memory needed for buffer, stores the buffer size in that memory + */ +int set_required_buffer_size(struct wmi_device *wdev, u64 length) +{ + struct wmi_block *wblock; + + wblock = container_of(wdev, struct wmi_block, dev); + wblock->req_buf_size = length; + + return 0; +} +EXPORT_SYMBOL_GPL(set_required_buffer_size); + /** * wmi_evaluate_method - Evaluate a WMI method * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba @@ -201,6 +226,28 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) { + struct wmi_block *wblock = NULL; + + if (!find_guid(guid_string, &wblock)) + return AE_ERROR; + return wmidev_evaluate_method(&wblock->dev, instance, method_id, + in, out); +} +EXPORT_SYMBOL_GPL(wmi_evaluate_method); + +/** + * wmidev_evaluate_method - Evaluate a WMI method + * @wdev: A wmi bus device from a driver + * @instance: Instance index + * @method_id: Method ID to call + * &in: Buffer containing input for the method call + * &out: Empty buffer to return the method results + * + * Call an ACPI-WMI method + */ +acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, + u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) +{ struct guid_block *block = NULL; struct wmi_block *wblock = NULL; acpi_handle handle; @@ -209,9 +256,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) union acpi_object params[3]; char method[5] = "WM"; - if (!find_guid(guid_string, &wblock)) - return AE_ERROR; - + wblock = container_of(wdev, struct wmi_block, dev); block = &wblock->gblock; handle = wblock->acpi_device->handle; @@ -246,7 +291,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) return status; } -EXPORT_SYMBOL_GPL(wmi_evaluate_method); +EXPORT_SYMBOL_GPL(wmidev_evaluate_method); static acpi_status __query_block(struct wmi_block *wblock, u8 instance, struct acpi_buffer *out) @@ -348,23 +393,6 @@ union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance) } EXPORT_SYMBOL_GPL(wmidev_block_query); -struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev, - const char *guid_string) -{ - struct wmi_block *this_wb = container_of(wdev, struct wmi_block, dev); - struct wmi_block *other_wb; - - if (!find_guid(guid_string, &other_wb)) - return NULL; - - if (other_wb->acpi_device != this_wb->acpi_device) - return NULL; - - get_device(&other_wb->dev.dev); - return &other_wb->dev; -} -EXPORT_SYMBOL_GPL(wmidev_get_other_guid); - /** * wmi_set_block - Write to a WMI block * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba @@ -761,6 +789,113 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver) return 0; } +static int wmi_char_open(struct inode *inode, struct file *filp) +{ + const char *driver_name = filp->f_path.dentry->d_iname; + struct wmi_block *wblock = NULL; + struct wmi_block *next = NULL; + + list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { + if (!wblock->dev.dev.driver) + continue; + if (strcmp(driver_name, wblock->dev.dev.driver->name) == 0) { + filp->private_data = wblock; + break; + } + } + + if (!filp->private_data) + return -ENODEV; + + return nonseekable_open(inode, filp); +} + +static ssize_t wmi_char_read(struct file *filp, char __user *buffer, + size_t length, loff_t *offset) +{ + struct wmi_block *wblock = filp->private_data; + + return simple_read_from_buffer(buffer, length, offset, + &wblock->req_buf_size, + sizeof(wblock->req_buf_size)); +} + +static long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct wmi_ioctl_buffer __user *input = + (struct wmi_ioctl_buffer __user *) arg; + struct wmi_block *wblock = filp->private_data; + struct wmi_ioctl_buffer *buf = NULL; + struct wmi_driver *wdriver = NULL; + int ret; + + if (_IOC_TYPE(cmd) != WMI_IOC) + return -ENOTTY; + + /* make sure we're not calling a higher instance than exists*/ + if (_IOC_NR(cmd) >= wblock->gblock.instance_count) + return -EINVAL; + + mutex_lock(&wblock->char_mutex); + buf = wblock->handler_data; + if (get_user(buf->length, &input->length)) { + dev_dbg(&wblock->dev.dev, "Read length from user failed\n"); + ret = -EFAULT; + goto out_ioctl; + } + /* if it's too small, abort */ + if (buf->length < wblock->req_buf_size) { + dev_err(&wblock->dev.dev, + "Buffer %lld too small, need at least %lld\n", + buf->length, wblock->req_buf_size); + ret = -EINVAL; + goto out_ioctl; + } + /* if it's too big, warn, driver will only use what is needed */ + if (buf->length > wblock->req_buf_size) + dev_warn(&wblock->dev.dev, + "Buffer %lld is bigger than required %lld\n", + buf->length, wblock->req_buf_size); + + /* copy the structure from userspace */ + if (copy_from_user(buf, input, wblock->req_buf_size)) { + dev_dbg(&wblock->dev.dev, "Copy %llu from user failed\n", + wblock->req_buf_size); + ret = -EFAULT; + goto out_ioctl; + } + + /* let the driver do any filtering and do the call */ + wdriver = container_of(wblock->dev.dev.driver, + struct wmi_driver, driver); + if (!try_module_get(wdriver->driver.owner)) { + ret = -EBUSY; + goto out_ioctl; + } + ret = wdriver->filter_callback(&wblock->dev, cmd, buf); + module_put(wdriver->driver.owner); + if (ret) + goto out_ioctl; + + /* return the result (only up to our internal buffer size) */ + if (copy_to_user(input, buf, wblock->req_buf_size)) { + dev_dbg(&wblock->dev.dev, "Copy %llu to user failed\n", + wblock->req_buf_size); + ret = -EFAULT; + } + +out_ioctl: + mutex_unlock(&wblock->char_mutex); + return ret; +} + +static const struct file_operations wmi_fops = { + .owner = THIS_MODULE, + .read = wmi_char_read, + .open = wmi_char_open, + .unlocked_ioctl = wmi_ioctl, + .compat_ioctl = wmi_ioctl, +}; static int wmi_dev_probe(struct device *dev) { @@ -768,16 +903,63 @@ static int wmi_dev_probe(struct device *dev) struct wmi_driver *wdriver = container_of(dev->driver, struct wmi_driver, driver); int ret = 0; + int count; + char *buf; if (ACPI_FAILURE(wmi_method_enable(wblock, 1))) dev_warn(dev, "failed to enable device -- probing anyway\n"); if (wdriver->probe) { ret = wdriver->probe(dev_to_wdev(dev)); - if (ret != 0 && ACPI_FAILURE(wmi_method_enable(wblock, 0))) - dev_warn(dev, "failed to disable device\n"); + if (ret != 0) + goto probe_failure; + } + + /* driver wants a character device made */ + if (wdriver->filter_callback) { + /* check that required buffer size declared by driver or MOF */ + if (!wblock->req_buf_size) { + dev_err(&wblock->dev.dev, + "Required buffer size not set\n"); + ret = -EINVAL; + goto probe_failure; + } + + count = get_order(wblock->req_buf_size); + wblock->handler_data = (void *)__get_free_pages(GFP_KERNEL, + count); + if (!wblock->handler_data) { + ret = -ENOMEM; + goto probe_failure; + } + + buf = kmalloc(strlen(wdriver->driver.name) + 4, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto probe_string_failure; + } + sprintf(buf, "wmi/%s", wdriver->driver.name); + wblock->char_dev.minor = MISC_DYNAMIC_MINOR; + wblock->char_dev.name = buf; + wblock->char_dev.fops = &wmi_fops; + wblock->char_dev.mode = 0444; + ret = misc_register(&wblock->char_dev); + if (ret) { + dev_warn(dev, "failed to register char dev: %d", ret); + ret = -ENOMEM; + goto probe_misc_failure; + } } + return 0; + +probe_misc_failure: + kfree(buf); +probe_string_failure: + kfree(wblock->handler_data); +probe_failure: + if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) + dev_warn(dev, "failed to disable device\n"); return ret; } @@ -788,6 +970,13 @@ static int wmi_dev_remove(struct device *dev) container_of(dev->driver, struct wmi_driver, driver); int ret = 0; + if (wdriver->filter_callback) { + misc_deregister(&wblock->char_dev); + kfree(wblock->char_dev.name); + free_pages((unsigned long)wblock->handler_data, + get_order(wblock->req_buf_size)); + } + if (wdriver->remove) ret = wdriver->remove(dev_to_wdev(dev)); @@ -844,6 +1033,7 @@ static int wmi_create_device(struct device *wmi_bus_dev, if (gblock->flags & ACPI_WMI_METHOD) { wblock->dev.dev.type = &wmi_type_method; + mutex_init(&wblock->char_mutex); goto out_init; } @@ -1145,7 +1335,7 @@ static int acpi_wmi_remove(struct platform_device *device) acpi_remove_address_space_handler(acpi_device->handle, ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); wmi_free_devices(acpi_device); - device_unregister((struct device *)dev_get_drvdata(&device->dev)); + device_destroy(&wmi_bus_class, MKDEV(0, 0)); return 0; } @@ -1199,7 +1389,7 @@ static int acpi_wmi_probe(struct platform_device *device) return 0; err_remove_busdev: - device_unregister(wmi_bus_dev); + device_destroy(&wmi_bus_class, MKDEV(0, 0)); err_remove_notify_handler: acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY, @@ -1264,8 +1454,8 @@ err_unreg_class: static void __exit acpi_wmi_exit(void) { platform_driver_unregister(&acpi_wmi_driver); - class_unregister(&wmi_bus_class); bus_unregister(&wmi_bus_type); + class_unregister(&wmi_bus_class); } subsys_initcall(acpi_wmi_init); diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 5e58f5ec0a28..2f615b7f1c9f 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -905,16 +905,6 @@ config FB_LEO This is the frame buffer device driver for the SBUS-based Sun ZX (leo) frame buffer cards. -config FB_IGA - bool "IGA 168x display support" - depends on (FB = y) && SPARC32 - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - help - This is the framebuffer device for the INTERGRAPHICS 1680 and - successor frame buffer cards. - config FB_XVR500 bool "Sun XVR-500 3DLABS Wildcat support" depends on (FB = y) && PCI && SPARC64 diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index 8895536a20d6..115961e0721b 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -65,7 +65,6 @@ obj-$(CONFIG_FB_HGA) += hgafb.o obj-$(CONFIG_FB_XVR500) += sunxvr500.o obj-$(CONFIG_FB_XVR2500) += sunxvr2500.o obj-$(CONFIG_FB_XVR1000) += sunxvr1000.o -obj-$(CONFIG_FB_IGA) += igafb.o obj-$(CONFIG_FB_APOLLO) += dnfb.o obj-$(CONFIG_FB_Q40) += q40fb.o obj-$(CONFIG_FB_TGA) += tgafb.o diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c index 3ec72f19114b..a9a8272f7a6e 100644 --- a/drivers/video/fbdev/aty/atyfb_base.c +++ b/drivers/video/fbdev/aty/atyfb_base.c @@ -2272,10 +2272,10 @@ static void aty_bl_exit(struct backlight_device *bd) static void aty_calc_mem_refresh(struct atyfb_par *par, int xclk) { - const int ragepro_tbl[] = { + static const int ragepro_tbl[] = { 44, 50, 55, 66, 75, 80, 100 }; - const int ragexl_tbl[] = { + static const int ragexl_tbl[] = { 50, 66, 75, 83, 90, 95, 100, 105, 110, 115, 120, 125, 133, 143, 166 }; diff --git a/drivers/video/fbdev/aty/radeon_base.c b/drivers/video/fbdev/aty/radeon_base.c index 1e2ec360f8c1..4d77daeecf99 100644 --- a/drivers/video/fbdev/aty/radeon_base.c +++ b/drivers/video/fbdev/aty/radeon_base.c @@ -1454,9 +1454,9 @@ static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_reg /* * Timer function for delayed LVDS panel power up/down */ -static void radeon_lvds_timer_func(unsigned long data) +static void radeon_lvds_timer_func(struct timer_list *t) { - struct radeonfb_info *rinfo = (struct radeonfb_info *)data; + struct radeonfb_info *rinfo = from_timer(rinfo, t, lvds_timer); radeon_engine_idle(); @@ -1534,7 +1534,7 @@ void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode, static void radeon_calc_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs *regs, unsigned long freq) { - const struct { + static const struct { int divider; int bitvalue; } *post_div, @@ -2291,9 +2291,7 @@ static int radeonfb_pci_register(struct pci_dev *pdev, rinfo->pdev = pdev; spin_lock_init(&rinfo->reg_lock); - init_timer(&rinfo->lvds_timer); - rinfo->lvds_timer.function = radeon_lvds_timer_func; - rinfo->lvds_timer.data = (unsigned long)rinfo; + timer_setup(&rinfo->lvds_timer, radeon_lvds_timer_func, 0); c1 = ent->device >> 8; c2 = ent->device & 0xff; diff --git a/drivers/video/fbdev/aty/radeon_pm.c b/drivers/video/fbdev/aty/radeon_pm.c index f7c253dd5899..7137c12cbcee 100644 --- a/drivers/video/fbdev/aty/radeon_pm.c +++ b/drivers/video/fbdev/aty/radeon_pm.c @@ -1208,9 +1208,11 @@ static void radeon_pm_enable_dll_m10(struct radeonfb_info *rinfo) case 1: if (mc & 0x4) break; + /* fall through */ case 2: dll_sleep_mask |= MDLL_R300_RDCK__MRDCKB_SLEEP; dll_reset_mask |= MDLL_R300_RDCK__MRDCKB_RESET; + /* fall through */ case 0: dll_sleep_mask |= MDLL_R300_RDCK__MRDCKA_SLEEP; dll_reset_mask |= MDLL_R300_RDCK__MRDCKA_RESET; @@ -1219,6 +1221,7 @@ static void radeon_pm_enable_dll_m10(struct radeonfb_info *rinfo) case 1: if (!(mc & 0x4)) break; + /* fall through */ case 2: dll_sleep_mask |= MDLL_R300_RDCK__MRDCKD_SLEEP; dll_reset_mask |= MDLL_R300_RDCK__MRDCKD_RESET; diff --git a/drivers/video/fbdev/au1200fb.c b/drivers/video/fbdev/au1200fb.c index 5f04b4096c42..87d5a62bf6ca 100644 --- a/drivers/video/fbdev/au1200fb.c +++ b/drivers/video/fbdev/au1200fb.c @@ -1518,7 +1518,7 @@ static irqreturn_t au1200fb_handle_irq(int irq, void* dev_id) static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev) { struct fb_info *fbi = fbdev->fb_info; - int bpp; + int bpp, ret; fbi->fbops = &au1200fb_fb_ops; @@ -1546,15 +1546,14 @@ static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev) } fbi->pseudo_palette = kcalloc(16, sizeof(u32), GFP_KERNEL); - if (!fbi->pseudo_palette) { + if (!fbi->pseudo_palette) return -ENOMEM; - } - if (fb_alloc_cmap(&fbi->cmap, AU1200_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { + ret = fb_alloc_cmap(&fbi->cmap, AU1200_LCD_NBR_PALETTE_ENTRIES, 0); + if (ret < 0) { print_err("Fail to allocate colormap (%d entries)", - AU1200_LCD_NBR_PALETTE_ENTRIES); - kfree(fbi->pseudo_palette); - return -EFAULT; + AU1200_LCD_NBR_PALETTE_ENTRIES); + return ret; } strncpy(fbi->fix.id, "AU1200", sizeof(fbi->fix.id)); @@ -1668,10 +1667,6 @@ static int au1200fb_drv_probe(struct platform_device *dev) printk(DRIVER_NAME ": Panel %d %s\n", panel_index, panel->name); printk(DRIVER_NAME ": Win %d %s\n", window_index, win->name); - /* shut gcc up */ - ret = 0; - fbdev = NULL; - for (plane = 0; plane < device_count; ++plane) { bpp = winbpp(win->w[plane].mode_winctrl1); if (win->w[plane].xres == 0) @@ -1681,8 +1676,10 @@ static int au1200fb_drv_probe(struct platform_device *dev) fbi = framebuffer_alloc(sizeof(struct au1200fb_device), &dev->dev); - if (!fbi) + if (!fbi) { + ret = -ENOMEM; goto failed; + } _au1200fb_infos[plane] = fbi; fbdev = fbi->par; @@ -1701,7 +1698,8 @@ static int au1200fb_drv_probe(struct platform_device *dev) if (!fbdev->fb_mem) { print_err("fail to allocate frambuffer (size: %dK))", fbdev->fb_len / 1024); - return -ENOMEM; + ret = -ENOMEM; + goto failed; } /* @@ -1718,7 +1716,8 @@ static int au1200fb_drv_probe(struct platform_device *dev) print_dbg("phys=0x%08x, size=%dK", fbdev->fb_phys, fbdev->fb_len / 1024); /* Init FB data */ - if ((ret = au1200fb_init_fbinfo(fbdev)) < 0) + ret = au1200fb_init_fbinfo(fbdev); + if (ret < 0) goto failed; /* Register new framebuffer */ @@ -1758,21 +1757,26 @@ static int au1200fb_drv_probe(struct platform_device *dev) return 0; failed: - /* NOTE: This only does the current plane/window that failed; others are still active */ - if (fbi) { + for (plane = 0; plane < device_count; ++plane) { + fbi = _au1200fb_infos[plane]; + if (!fbi) + break; + + /* Clean up all probe data */ + unregister_framebuffer(fbi); if (fbi->cmap.len != 0) fb_dealloc_cmap(&fbi->cmap); kfree(fbi->pseudo_palette); + + framebuffer_release(fbi); + _au1200fb_infos[plane] = NULL; } - if (plane == 0) - free_irq(AU1200_LCD_INT, (void*)dev); return ret; } static int au1200fb_drv_remove(struct platform_device *dev) { struct au1200fb_platdata *pd = platform_get_drvdata(dev); - struct au1200fb_device *fbdev; struct fb_info *fbi; int plane; @@ -1781,7 +1785,6 @@ static int au1200fb_drv_remove(struct platform_device *dev) for (plane = 0; plane < device_count; ++plane) { fbi = _au1200fb_infos[plane]; - fbdev = fbi->par; /* Clean up all probe data */ unregister_framebuffer(fbi); diff --git a/drivers/video/fbdev/cirrusfb.c b/drivers/video/fbdev/cirrusfb.c index d992aa5eb3f0..b3be06dd2908 100644 --- a/drivers/video/fbdev/cirrusfb.c +++ b/drivers/video/fbdev/cirrusfb.c @@ -1477,10 +1477,12 @@ static void init_vgachip(struct fb_info *info) mdelay(100); /* mode */ vga_wgfx(cinfo->regbase, CL_GR31, 0x00); - case BT_GD5480: /* fall through */ + /* fall through */ + case BT_GD5480: /* from Klaus' NetBSD driver: */ vga_wgfx(cinfo->regbase, CL_GR2F, 0x00); - case BT_ALPINE: /* fall through */ + /* fall through */ + case BT_ALPINE: /* put blitter into 542x compat */ vga_wgfx(cinfo->regbase, CL_GR33, 0x00); break; diff --git a/drivers/video/fbdev/controlfb.h b/drivers/video/fbdev/controlfb.h index 6026c60fc100..261522fabdac 100644 --- a/drivers/video/fbdev/controlfb.h +++ b/drivers/video/fbdev/controlfb.h @@ -141,5 +141,7 @@ static struct max_cmodes control_mac_modes[] = { {{ 1, 2}}, /* 1152x870, 75Hz */ {{ 0, 1}}, /* 1280x960, 75Hz */ {{ 0, 1}}, /* 1280x1024, 75Hz */ + {{ 1, 2}}, /* 1152x768, 60Hz */ + {{ 0, 1}}, /* 1600x1024, 60Hz */ }; diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 04612f938bab..929ca472c524 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -395,10 +395,10 @@ static void fb_flashcursor(struct work_struct *work) console_unlock(); } -static void cursor_timer_handler(unsigned long dev_addr) +static void cursor_timer_handler(struct timer_list *t) { - struct fb_info *info = (struct fb_info *) dev_addr; - struct fbcon_ops *ops = info->fbcon_par; + struct fbcon_ops *ops = from_timer(ops, t, cursor_timer); + struct fb_info *info = ops->info; queue_work(system_power_efficient_wq, &info->queue); mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies); @@ -414,8 +414,7 @@ static void fbcon_add_cursor_timer(struct fb_info *info) if (!info->queue.func) INIT_WORK(&info->queue, fb_flashcursor); - setup_timer(&ops->cursor_timer, cursor_timer_handler, - (unsigned long) info); + timer_setup(&ops->cursor_timer, cursor_timer_handler, 0); mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies); ops->flags |= FBCON_FLAGS_CURSOR_TIMER; } @@ -714,6 +713,7 @@ static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info, if (!err) { ops->cur_blink_jiffies = HZ / 5; + ops->info = info; info->fbcon_par = ops; if (vc) @@ -962,6 +962,7 @@ static const char *fbcon_startup(void) ops->graphics = 1; ops->cur_rotate = -1; ops->cur_blink_jiffies = HZ / 5; + ops->info = info; info->fbcon_par = ops; if (initial_rotation != -1) p->con_rotate = initial_rotation; diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h index 18f3ac144237..9f7744fbc962 100644 --- a/drivers/video/fbdev/core/fbcon.h +++ b/drivers/video/fbdev/core/fbcon.h @@ -69,6 +69,7 @@ struct fbcon_ops { struct timer_list cursor_timer; /* Cursor timer */ struct fb_cursor cursor_state; struct display *p; + struct fb_info *info; int currcon; /* Current VC. */ int cur_blink_jiffies; int cursor_flash; diff --git a/drivers/video/fbdev/dnfb.c b/drivers/video/fbdev/dnfb.c index 7b1492d34e98..5505fa00c634 100644 --- a/drivers/video/fbdev/dnfb.c +++ b/drivers/video/fbdev/dnfb.c @@ -115,7 +115,7 @@ static struct fb_ops dn_fb_ops = { .fb_imageblit = cfb_imageblit, }; -struct fb_var_screeninfo dnfb_var = { +static const struct fb_var_screeninfo dnfb_var = { .xres = 1280, .yres = 1024, .xres_virtual = 2048, @@ -242,16 +242,13 @@ static int dnfb_probe(struct platform_device *dev) info->screen_base = (u_char *) info->fix.smem_start; err = fb_alloc_cmap(&info->cmap, 2, 0); - if (err < 0) { - framebuffer_release(info); - return err; - } + if (err < 0) + goto release_framebuffer; err = register_framebuffer(info); if (err < 0) { fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - return err; + goto release_framebuffer; } platform_set_drvdata(dev, info); @@ -265,6 +262,10 @@ static int dnfb_probe(struct platform_device *dev) printk("apollo frame buffer alive and kicking !\n"); return err; + +release_framebuffer: + framebuffer_release(info); + return err; } static struct platform_driver dnfb_driver = { diff --git a/drivers/video/fbdev/goldfishfb.c b/drivers/video/fbdev/goldfishfb.c index 7f6c9e6cfc6c..3b70044773b6 100644 --- a/drivers/video/fbdev/goldfishfb.c +++ b/drivers/video/fbdev/goldfishfb.c @@ -304,12 +304,18 @@ static int goldfish_fb_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id goldfish_fb_of_match[] = { + { .compatible = "google,goldfish-fb", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_fb_of_match); static struct platform_driver goldfish_fb_driver = { .probe = goldfish_fb_probe, .remove = goldfish_fb_remove, .driver = { - .name = "goldfish_fb" + .name = "goldfish_fb", + .of_match_table = goldfish_fb_of_match, } }; diff --git a/drivers/video/fbdev/igafb.c b/drivers/video/fbdev/igafb.c deleted file mode 100644 index 486f18897414..000000000000 --- a/drivers/video/fbdev/igafb.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - * linux/drivers/video/igafb.c -- Frame buffer device for IGA 1682 - * - * Copyright (C) 1998 Vladimir Roganov and Gleb Raiko - * - * This driver is partly based on the Frame buffer device for ATI Mach64 - * and partially on VESA-related code. - * - * Copyright (C) 1997-1998 Geert Uytterhoeven - * Copyright (C) 1998 Bernd Harries - * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive for - * more details. - */ - -/****************************************************************************** - - TODO: - Despite of IGA Card has advanced graphic acceleration, - initial version is almost dummy and does not support it. - Support for video modes and acceleration must be added - together with accelerated X-Windows driver implementation. - - Most important thing at this moment is that we have working - JavaEngine1 console & X with new console interface. - -******************************************************************************/ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/fb.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/nvram.h> - -#include <asm/io.h> - -#ifdef CONFIG_SPARC -#include <asm/prom.h> -#include <asm/pcic.h> -#endif - -#include <video/iga.h> - -struct pci_mmap_map { - unsigned long voff; - unsigned long poff; - unsigned long size; - unsigned long prot_flag; - unsigned long prot_mask; -}; - -struct iga_par { - struct pci_mmap_map *mmap_map; - unsigned long frame_buffer_phys; - unsigned long io_base; -}; - -struct fb_info fb_info; - -struct fb_fix_screeninfo igafb_fix __initdata = { - .id = "IGA 1682", - .type = FB_TYPE_PACKED_PIXELS, - .mmio_len = 1000 -}; - -struct fb_var_screeninfo default_var = { - /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */ - .xres = 640, - .yres = 480, - .xres_virtual = 640, - .yres_virtual = 480, - .bits_per_pixel = 8, - .red = {0, 8, 0 }, - .green = {0, 8, 0 }, - .blue = {0, 8, 0 }, - .height = -1, - .width = -1, - .accel_flags = FB_ACCEL_NONE, - .pixclock = 39722, - .left_margin = 48, - .right_margin = 16, - .upper_margin = 33, - .lower_margin = 10, - .hsync_len = 96, - .vsync_len = 2, - .vmode = FB_VMODE_NONINTERLACED -}; - -#ifdef CONFIG_SPARC -struct fb_var_screeninfo default_var_1024x768 __initdata = { - /* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */ - .xres = 1024, - .yres = 768, - .xres_virtual = 1024, - .yres_virtual = 768, - .bits_per_pixel = 8, - .red = {0, 8, 0 }, - .green = {0, 8, 0 }, - .blue = {0, 8, 0 }, - .height = -1, - .width = -1, - .accel_flags = FB_ACCEL_NONE, - .pixclock = 12699, - .left_margin = 176, - .right_margin = 16, - .upper_margin = 28, - .lower_margin = 1, - .hsync_len = 96, - .vsync_len = 3, - .vmode = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED -}; - -struct fb_var_screeninfo default_var_1152x900 __initdata = { - /* 1152x900, 76 Hz, Non-Interlaced (110.0 MHz dotclock) */ - .xres = 1152, - .yres = 900, - .xres_virtual = 1152, - .yres_virtual = 900, - .bits_per_pixel = 8, - .red = { 0, 8, 0 }, - .green = { 0, 8, 0 }, - .blue = { 0, 8, 0 }, - .height = -1, - .width = -1, - .accel_flags = FB_ACCEL_NONE, - .pixclock = 9091, - .left_margin = 234, - .right_margin = 24, - .upper_margin = 34, - .lower_margin = 3, - .hsync_len = 100, - .vsync_len = 3, - .vmode = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED -}; - -struct fb_var_screeninfo default_var_1280x1024 __initdata = { - /* 1280x1024, 75 Hz, Non-Interlaced (135.00 MHz dotclock) */ - .xres = 1280, - .yres = 1024, - .xres_virtual = 1280, - .yres_virtual = 1024, - .bits_per_pixel = 8, - .red = {0, 8, 0 }, - .green = {0, 8, 0 }, - .blue = {0, 8, 0 }, - .height = -1, - .width = -1, - .accel_flags = 0, - .pixclock = 7408, - .left_margin = 248, - .right_margin = 16, - .upper_margin = 38, - .lower_margin = 1, - .hsync_len = 144, - .vsync_len = 3, - .vmode = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED -}; - -/* - * Memory-mapped I/O functions for Sparc PCI - * - * On sparc we happen to access I/O with memory mapped functions too. - */ -#define pci_inb(par, reg) readb(par->io_base+(reg)) -#define pci_outb(par, val, reg) writeb(val, par->io_base+(reg)) - -static inline unsigned int iga_inb(struct iga_par *par, unsigned int reg, - unsigned int idx) -{ - pci_outb(par, idx, reg); - return pci_inb(par, reg + 1); -} - -static inline void iga_outb(struct iga_par *par, unsigned char val, - unsigned int reg, unsigned int idx ) -{ - pci_outb(par, idx, reg); - pci_outb(par, val, reg+1); -} - -#endif /* CONFIG_SPARC */ - -/* - * Very important functionality for the JavaEngine1 computer: - * make screen border black (usign special IGA registers) - */ -static void iga_blank_border(struct iga_par *par) -{ - int i; -#if 0 - /* - * PROM does this for us, so keep this code as a reminder - * about required read from 0x3DA and writing of 0x20 in the end. - */ - (void) pci_inb(par, 0x3DA); /* required for every access */ - pci_outb(par, IGA_IDX_VGA_OVERSCAN, IGA_ATTR_CTL); - (void) pci_inb(par, IGA_ATTR_CTL+1); - pci_outb(par, 0x38, IGA_ATTR_CTL); - pci_outb(par, 0x20, IGA_ATTR_CTL); /* re-enable visual */ -#endif - /* - * This does not work as it was designed because the overscan - * color is looked up in the palette. Therefore, under X11 - * overscan changes color. - */ - for (i=0; i < 3; i++) - iga_outb(par, 0, IGA_EXT_CNTRL, IGA_IDX_OVERSCAN_COLOR + i); -} - -#ifdef CONFIG_SPARC -static int igafb_mmap(struct fb_info *info, - struct vm_area_struct *vma) -{ - struct iga_par *par = (struct iga_par *)info->par; - unsigned int size, page, map_size = 0; - unsigned long map_offset = 0; - int i; - - if (!par->mmap_map) - return -ENXIO; - - size = vma->vm_end - vma->vm_start; - - /* Each page, see which map applies */ - for (page = 0; page < size; ) { - map_size = 0; - for (i = 0; par->mmap_map[i].size; i++) { - unsigned long start = par->mmap_map[i].voff; - unsigned long end = start + par->mmap_map[i].size; - unsigned long offset = (vma->vm_pgoff << PAGE_SHIFT) + page; - - if (start > offset) - continue; - if (offset >= end) - continue; - - map_size = par->mmap_map[i].size - (offset - start); - map_offset = par->mmap_map[i].poff + (offset - start); - break; - } - if (!map_size) { - page += PAGE_SIZE; - continue; - } - if (page + map_size > size) - map_size = size - page; - - pgprot_val(vma->vm_page_prot) &= ~(par->mmap_map[i].prot_mask); - pgprot_val(vma->vm_page_prot) |= par->mmap_map[i].prot_flag; - - if (remap_pfn_range(vma, vma->vm_start + page, - map_offset >> PAGE_SHIFT, map_size, vma->vm_page_prot)) - return -EAGAIN; - - page += map_size; - } - - if (!map_size) - return -EINVAL; - - vma->vm_flags |= VM_IO; - return 0; -} -#endif /* CONFIG_SPARC */ - -static int igafb_setcolreg(unsigned regno, unsigned red, unsigned green, - unsigned blue, unsigned transp, - struct fb_info *info) -{ - /* - * Set a single color register. The values supplied are - * already rounded down to the hardware's capabilities - * (according to the entries in the `var' structure). Return - * != 0 for invalid regno. - */ - struct iga_par *par = (struct iga_par *)info->par; - - if (regno >= info->cmap.len) - return 1; - - pci_outb(par, regno, DAC_W_INDEX); - pci_outb(par, red, DAC_DATA); - pci_outb(par, green, DAC_DATA); - pci_outb(par, blue, DAC_DATA); - - if (regno < 16) { - switch (info->var.bits_per_pixel) { - case 16: - ((u16*)(info->pseudo_palette))[regno] = - (regno << 10) | (regno << 5) | regno; - break; - case 24: - ((u32*)(info->pseudo_palette))[regno] = - (regno << 16) | (regno << 8) | regno; - break; - case 32: - { int i; - i = (regno << 8) | regno; - ((u32*)(info->pseudo_palette))[regno] = (i << 16) | i; - } - break; - } - } - return 0; -} - -/* - * Framebuffer option structure - */ -static struct fb_ops igafb_ops = { - .owner = THIS_MODULE, - .fb_setcolreg = igafb_setcolreg, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = cfb_copyarea, - .fb_imageblit = cfb_imageblit, -#ifdef CONFIG_SPARC - .fb_mmap = igafb_mmap, -#endif -}; - -static int __init iga_init(struct fb_info *info, struct iga_par *par) -{ - char vramsz = iga_inb(par, IGA_EXT_CNTRL, IGA_IDX_EXT_BUS_CNTL) - & MEM_SIZE_ALIAS; - int video_cmap_len; - - switch (vramsz) { - case MEM_SIZE_1M: - info->fix.smem_len = 0x100000; - break; - case MEM_SIZE_2M: - info->fix.smem_len = 0x200000; - break; - case MEM_SIZE_4M: - case MEM_SIZE_RESERVED: - info->fix.smem_len = 0x400000; - break; - } - - if (info->var.bits_per_pixel > 8) - video_cmap_len = 16; - else - video_cmap_len = 256; - - info->fbops = &igafb_ops; - info->flags = FBINFO_DEFAULT; - - fb_alloc_cmap(&info->cmap, video_cmap_len, 0); - - if (register_framebuffer(info) < 0) - return 0; - - fb_info(info, "%s frame buffer device at 0x%08lx [%dMB VRAM]\n", - info->fix.id, par->frame_buffer_phys, info->fix.smem_len >> 20); - - iga_blank_border(par); - return 1; -} - -static int __init igafb_init(void) -{ - struct fb_info *info; - struct pci_dev *pdev; - struct iga_par *par; - unsigned long addr; - int size, iga2000 = 0; - - if (fb_get_options("igafb", NULL)) - return -ENODEV; - - pdev = pci_get_device(PCI_VENDOR_ID_INTERG, - PCI_DEVICE_ID_INTERG_1682, 0); - if (pdev == NULL) { - /* - * XXX We tried to use cyber2000fb.c for IGS 2000. - * But it does not initialize the chip in JavaStation-E, alas. - */ - pdev = pci_get_device(PCI_VENDOR_ID_INTERG, 0x2000, 0); - if(pdev == NULL) { - return -ENXIO; - } - iga2000 = 1; - } - /* We leak a reference here but as it cannot be unloaded this is - fine. If you write unload code remember to free it in unload */ - - size = sizeof(struct iga_par) + sizeof(u32)*16; - - info = framebuffer_alloc(size, &pdev->dev); - if (!info) { - printk("igafb_init: can't alloc fb_info\n"); - pci_dev_put(pdev); - return -ENOMEM; - } - - par = info->par; - - if ((addr = pdev->resource[0].start) == 0) { - printk("igafb_init: no memory start\n"); - kfree(info); - pci_dev_put(pdev); - return -ENXIO; - } - - if ((info->screen_base = ioremap(addr, 1024*1024*2)) == 0) { - printk("igafb_init: can't remap %lx[2M]\n", addr); - kfree(info); - pci_dev_put(pdev); - return -ENXIO; - } - - par->frame_buffer_phys = addr & PCI_BASE_ADDRESS_MEM_MASK; - -#ifdef CONFIG_SPARC - /* - * The following is sparc specific and this is why: - * - * IGS2000 has its I/O memory mapped and we want - * to generate memory cycles on PCI, e.g. do ioremap(), - * then readb/writeb() as in Documentation/io-mapping.txt. - * - * IGS1682 is more traditional, it responds to PCI I/O - * cycles, so we want to access it with inb()/outb(). - * - * On sparc, PCIC converts CPU memory access within - * phys window 0x3000xxxx into PCI I/O cycles. Therefore - * we may use readb/writeb to access them with IGS1682. - * - * We do not take io_base_phys from resource[n].start - * on IGS1682 because that chip is BROKEN. It does not - * have a base register for I/O. We just "know" what its - * I/O addresses are. - */ - if (iga2000) { - igafb_fix.mmio_start = par->frame_buffer_phys | 0x00800000; - } else { - igafb_fix.mmio_start = 0x30000000; /* XXX */ - } - if ((par->io_base = (int) ioremap(igafb_fix.mmio_start, igafb_fix.smem_len)) == 0) { - printk("igafb_init: can't remap %lx[4K]\n", igafb_fix.mmio_start); - iounmap((void *)info->screen_base); - kfree(info); - pci_dev_put(pdev); - return -ENXIO; - } - - /* - * Figure mmap addresses from PCI config space. - * We need two regions: for video memory and for I/O ports. - * Later one can add region for video coprocessor registers. - * However, mmap routine loops until size != 0, so we put - * one additional region with size == 0. - */ - - par->mmap_map = kzalloc(4 * sizeof(*par->mmap_map), GFP_ATOMIC); - if (!par->mmap_map) { - printk("igafb_init: can't alloc mmap_map\n"); - iounmap((void *)par->io_base); - iounmap(info->screen_base); - kfree(info); - pci_dev_put(pdev); - return -ENOMEM; - } - - /* - * Set default vmode and cmode from PROM properties. - */ - { - struct device_node *dp = pci_device_to_OF_node(pdev); - int node = dp->node; - int width = prom_getintdefault(node, "width", 1024); - int height = prom_getintdefault(node, "height", 768); - int depth = prom_getintdefault(node, "depth", 8); - switch (width) { - case 1024: - if (height == 768) - default_var = default_var_1024x768; - break; - case 1152: - if (height == 900) - default_var = default_var_1152x900; - break; - case 1280: - if (height == 1024) - default_var = default_var_1280x1024; - break; - default: - break; - } - - switch (depth) { - case 8: - default_var.bits_per_pixel = 8; - break; - case 16: - default_var.bits_per_pixel = 16; - break; - case 24: - default_var.bits_per_pixel = 24; - break; - case 32: - default_var.bits_per_pixel = 32; - break; - default: - break; - } - } - -#endif - igafb_fix.smem_start = (unsigned long) info->screen_base; - igafb_fix.line_length = default_var.xres*(default_var.bits_per_pixel/8); - igafb_fix.visual = default_var.bits_per_pixel <= 8 ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR; - - info->var = default_var; - info->fix = igafb_fix; - info->pseudo_palette = (void *)(par + 1); - - if (!iga_init(info, par)) { - iounmap((void *)par->io_base); - iounmap(info->screen_base); - kfree(par->mmap_map); - kfree(info); - return -ENODEV; - } - -#ifdef CONFIG_SPARC - /* - * Add /dev/fb mmap values. - */ - - /* First region is for video memory */ - par->mmap_map[0].voff = 0x0; - par->mmap_map[0].poff = par->frame_buffer_phys & PAGE_MASK; - par->mmap_map[0].size = info->fix.smem_len & PAGE_MASK; - par->mmap_map[0].prot_mask = SRMMU_CACHE; - par->mmap_map[0].prot_flag = SRMMU_WRITE; - - /* Second region is for I/O ports */ - par->mmap_map[1].voff = par->frame_buffer_phys & PAGE_MASK; - par->mmap_map[1].poff = info->fix.smem_start & PAGE_MASK; - par->mmap_map[1].size = PAGE_SIZE * 2; /* X wants 2 pages */ - par->mmap_map[1].prot_mask = SRMMU_CACHE; - par->mmap_map[1].prot_flag = SRMMU_WRITE; -#endif /* CONFIG_SPARC */ - - return 0; -} - -static int __init igafb_setup(char *options) -{ - char *this_opt; - - if (!options || !*options) - return 0; - - while ((this_opt = strsep(&options, ",")) != NULL) { - } - return 0; -} - -module_init(igafb_init); -MODULE_LICENSE("GPL"); -static struct pci_device_id igafb_pci_tbl[] = { - { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_1682, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - { } -}; - -MODULE_DEVICE_TABLE(pci, igafb_pci_tbl); diff --git a/drivers/video/fbdev/intelfb/intelfbhw.c b/drivers/video/fbdev/intelfb/intelfbhw.c index d31ed4e2c46f..83fec573cceb 100644 --- a/drivers/video/fbdev/intelfb/intelfbhw.c +++ b/drivers/video/fbdev/intelfb/intelfbhw.c @@ -937,15 +937,11 @@ static int calc_pll_params(int index, int clock, u32 *retm1, u32 *retm2, { u32 m1, m2, n, p1, p2, n1, testm; u32 f_vco, p, p_best = 0, m, f_out = 0; - u32 err_max, err_target, err_best = 10000000; - u32 n_best = 0, m_best = 0, f_best, f_err; + u32 err_best = 10000000; + u32 n_best = 0, m_best = 0, f_err; u32 p_min, p_max, p_inc, div_max; struct pll_min_max *pll = &plls[index]; - /* Accept 0.5% difference, but aim for 0.1% */ - err_max = 5 * clock / 1000; - err_target = clock / 1000; - DBG_MSG("Clock is %d\n", clock); div_max = pll->max_vco / clock; @@ -992,7 +988,6 @@ static int calc_pll_params(int index, int clock, u32 *retm1, u32 *retm2, m_best = testm; n_best = n; p_best = p; - f_best = f_out; err_best = f_err; } } diff --git a/drivers/video/fbdev/matrox/matroxfb_base.c b/drivers/video/fbdev/matrox/matroxfb_base.c index b9b284d79631..838869c6490c 100644 --- a/drivers/video/fbdev/matrox/matroxfb_base.c +++ b/drivers/video/fbdev/matrox/matroxfb_base.c @@ -2056,7 +2056,7 @@ static int matroxfb_probe(struct pci_dev* pdev, const struct pci_device_id* dumm minfo = kzalloc(sizeof(*minfo), GFP_KERNEL); if (!minfo) - return -1; + return -ENOMEM; minfo->pcidev = pdev; minfo->dead = 0; diff --git a/drivers/video/fbdev/mxsfb.c b/drivers/video/fbdev/mxsfb.c index 7846f0e8bbbb..79b1dc7f042b 100644 --- a/drivers/video/fbdev/mxsfb.c +++ b/drivers/video/fbdev/mxsfb.c @@ -150,7 +150,7 @@ #define STMLCDIF_24BIT 3 /** pixel data bus to the display is of 24 bit width */ #define MXSFB_SYNC_DATA_ENABLE_HIGH_ACT (1 << 6) -#define MXSFB_SYNC_DOTCLK_FALLING_ACT (1 << 7) /* negtive edge sampling */ +#define MXSFB_SYNC_DOTCLK_FALLING_ACT (1 << 7) /* negative edge sampling */ enum mxsfb_devtype { MXSFB_V3, @@ -788,7 +788,16 @@ static int mxsfb_init_fbinfo_dt(struct mxsfb_info *host, if (vm.flags & DISPLAY_FLAGS_DE_HIGH) host->sync |= MXSFB_SYNC_DATA_ENABLE_HIGH_ACT; - if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) + + /* + * The PIXDATA flags of the display_flags enum are controller + * centric, e.g. NEGEDGE means drive data on negative edge. + * However, the drivers flag is display centric: Sample the + * data on negative (falling) edge. Therefore, check for the + * POSEDGE flag: + * drive on positive edge => sample on negative edge + */ + if (vm.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) host->sync |= MXSFB_SYNC_DOTCLK_FALLING_ACT; put_display_node: diff --git a/drivers/video/fbdev/omap/hwa742.c b/drivers/video/fbdev/omap/hwa742.c index a4ee65b8f918..6199d4806193 100644 --- a/drivers/video/fbdev/omap/hwa742.c +++ b/drivers/video/fbdev/omap/hwa742.c @@ -474,7 +474,7 @@ static void auto_update_complete(void *data) jiffies + HWA742_AUTO_UPDATE_TIME); } -static void hwa742_update_window_auto(unsigned long arg) +static void hwa742_update_window_auto(struct timer_list *unused) { LIST_HEAD(req_list); struct hwa742_request *last; @@ -1002,9 +1002,7 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode, hwa742.auto_update_window.height = fbdev->panel->y_res; hwa742.auto_update_window.format = 0; - init_timer(&hwa742.auto_update_timer); - hwa742.auto_update_timer.function = hwa742_update_window_auto; - hwa742.auto_update_timer.data = 0; + timer_setup(&hwa742.auto_update_timer, hwa742_update_window_auto, 0); hwa742.prev_color_mode = -1; hwa742.prev_flags = 0; diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dsi.c b/drivers/video/fbdev/omap2/omapfb/dss/dsi.c index 30d49f3800b3..8e1d60d48dbb 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dsi.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dsi.c @@ -3988,7 +3988,7 @@ static void dsi_update_screen_dispc(struct platform_device *dsidev) } #ifdef DSI_CATCH_MISSING_TE -static void dsi_te_timeout(unsigned long arg) +static void dsi_te_timeout(struct timer_list *unused) { DSSERR("TE not received for 250ms!\n"); } @@ -5298,9 +5298,7 @@ static int dsi_bind(struct device *dev, struct device *master, void *data) dsi_framedone_timeout_work_callback); #ifdef DSI_CATCH_MISSING_TE - init_timer(&dsi->te_timer); - dsi->te_timer.function = dsi_te_timeout; - dsi->te_timer.data = 0; + timer_setup(&dsi->te_timer, dsi_te_timeout, 0); #endif res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "proto"); diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c index 1d7c012f09db..e08e5664e330 100644 --- a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c @@ -1477,7 +1477,7 @@ static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size, static int omapfb_parse_vram_param(const char *param, int max_entries, unsigned long *sizes, unsigned long *paddrs) { - int fbnum; + unsigned int fbnum; unsigned long size; unsigned long paddr = 0; char *p, *start; diff --git a/drivers/video/fbdev/pxa3xx-gcu.c b/drivers/video/fbdev/pxa3xx-gcu.c index 933619da1a94..55fbb432c053 100644 --- a/drivers/video/fbdev/pxa3xx-gcu.c +++ b/drivers/video/fbdev/pxa3xx-gcu.c @@ -512,28 +512,26 @@ pxa3xx_gcu_mmap(struct file *file, struct vm_area_struct *vma) #ifdef PXA3XX_GCU_DEBUG_TIMER static struct timer_list pxa3xx_gcu_debug_timer; +static struct pxa3xx_gcu_priv *debug_timer_priv; -static void pxa3xx_gcu_debug_timedout(unsigned long ptr) +static void pxa3xx_gcu_debug_timedout(struct timer_list *unused) { - struct pxa3xx_gcu_priv *priv = (struct pxa3xx_gcu_priv *) ptr; + struct pxa3xx_gcu_priv *priv = debug_timer_priv; QERROR("Timer DUMP"); - /* init the timer structure */ - init_timer(&pxa3xx_gcu_debug_timer); - pxa3xx_gcu_debug_timer.function = pxa3xx_gcu_debug_timedout; - pxa3xx_gcu_debug_timer.data = ptr; - pxa3xx_gcu_debug_timer.expires = jiffies + 5*HZ; /* one second */ - - add_timer(&pxa3xx_gcu_debug_timer); + mod_timer(&pxa3xx_gcu_debug_timer, jiffies + 5 * HZ); } -static void pxa3xx_gcu_init_debug_timer(void) +static void pxa3xx_gcu_init_debug_timer(struct pxa3xx_gcu_priv *priv) { - pxa3xx_gcu_debug_timedout((unsigned long) &pxa3xx_gcu_debug_timer); + /* init the timer structure */ + debug_timer_priv = priv; + timer_setup(&pxa3xx_gcu_debug_timer, pxa3xx_gcu_debug_timedout, 0); + pxa3xx_gcu_debug_timedout(NULL); } #else -static inline void pxa3xx_gcu_init_debug_timer(void) {} +static inline void pxa3xx_gcu_init_debug_timer(struct pxa3xx_gcu_priv *priv) {} #endif static int @@ -670,7 +668,7 @@ static int pxa3xx_gcu_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); priv->resource_mem = r; pxa3xx_gcu_reset(priv); - pxa3xx_gcu_init_debug_timer(); + pxa3xx_gcu_init_debug_timer(priv); dev_info(dev, "registered @0x%p, DMA 0x%p (%d bytes), IRQ %d\n", (void *) r->start, (void *) priv->shared_phys, diff --git a/drivers/video/fbdev/sa1100fb.c b/drivers/video/fbdev/sa1100fb.c index fc2aaa5aca23..15ae50063296 100644 --- a/drivers/video/fbdev/sa1100fb.c +++ b/drivers/video/fbdev/sa1100fb.c @@ -323,13 +323,11 @@ sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, * according to the RGB bitfield information. */ if (regno < 16) { - u32 *pal = fbi->fb.pseudo_palette; - val = chan_to_field(red, &fbi->fb.var.red); val |= chan_to_field(green, &fbi->fb.var.green); val |= chan_to_field(blue, &fbi->fb.var.blue); - pal[regno] = val; + fbi->pseudo_palette[regno] = val; ret = 0; } break; @@ -1132,12 +1130,10 @@ static struct sa1100fb_info *sa1100fb_init_fbinfo(struct device *dev) struct sa1100fb_info *fbi; unsigned i; - fbi = kmalloc(sizeof(struct sa1100fb_info) + sizeof(u32) * 16, - GFP_KERNEL); + fbi = devm_kzalloc(dev, sizeof(struct sa1100fb_info), GFP_KERNEL); if (!fbi) return NULL; - memset(fbi, 0, sizeof(struct sa1100fb_info)); fbi->dev = dev; strcpy(fbi->fb.fix.id, SA1100_NAME); @@ -1159,7 +1155,7 @@ static struct sa1100fb_info *sa1100fb_init_fbinfo(struct device *dev) fbi->fb.fbops = &sa1100fb_ops; fbi->fb.flags = FBINFO_DEFAULT; fbi->fb.monspecs = monspecs; - fbi->fb.pseudo_palette = (fbi + 1); + fbi->fb.pseudo_palette = fbi->pseudo_palette; fbi->rgb[RGB_4] = &rgb_4; fbi->rgb[RGB_8] = &rgb_8; @@ -1218,48 +1214,42 @@ static int sa1100fb_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (irq < 0 || !res) + if (irq < 0) return -EINVAL; - if (!request_mem_region(res->start, resource_size(res), "LCD")) - return -EBUSY; - fbi = sa1100fb_init_fbinfo(&pdev->dev); - ret = -ENOMEM; if (!fbi) - goto failed; - - fbi->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(fbi->clk)) { - ret = PTR_ERR(fbi->clk); - fbi->clk = NULL; - goto failed; - } + return -ENOMEM; - fbi->base = ioremap(res->start, resource_size(res)); - if (!fbi->base) - goto failed; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + fbi->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fbi->base)) + return PTR_ERR(fbi->base); - /* Initialize video memory */ - ret = sa1100fb_map_video_memory(fbi); - if (ret) - goto failed; + fbi->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(fbi->clk)) + return PTR_ERR(fbi->clk); - ret = request_irq(irq, sa1100fb_handle_irq, 0, "LCD", fbi); + ret = devm_request_irq(&pdev->dev, irq, sa1100fb_handle_irq, 0, + "LCD", fbi); if (ret) { dev_err(&pdev->dev, "request_irq failed: %d\n", ret); - goto failed; + return ret; } if (machine_is_shannon()) { - ret = gpio_request_one(SHANNON_GPIO_DISP_EN, + ret = devm_gpio_request_one(&pdev->dev, SHANNON_GPIO_DISP_EN, GPIOF_OUT_INIT_LOW, "display enable"); if (ret) - goto err_free_irq; + return ret; } + /* Initialize video memory */ + ret = sa1100fb_map_video_memory(fbi); + if (ret) + return ret; + /* * This makes sure that our colour bitfield * descriptors are correctly initialised. @@ -1269,8 +1259,11 @@ static int sa1100fb_probe(struct platform_device *pdev) platform_set_drvdata(pdev, fbi); ret = register_framebuffer(&fbi->fb); - if (ret < 0) - goto err_reg_fb; + if (ret < 0) { + dma_free_wc(fbi->dev, fbi->map_size, fbi->map_cpu, + fbi->map_dma); + return ret; + } #ifdef CONFIG_CPU_FREQ fbi->freq_transition.notifier_call = sa1100fb_freq_transition; @@ -1281,20 +1274,6 @@ static int sa1100fb_probe(struct platform_device *pdev) /* This driver cannot be unloaded at the moment */ return 0; - - err_reg_fb: - if (machine_is_shannon()) - gpio_free(SHANNON_GPIO_DISP_EN); - err_free_irq: - free_irq(irq, fbi); - failed: - if (fbi) - iounmap(fbi->base); - if (fbi->clk) - clk_put(fbi->clk); - kfree(fbi); - release_mem_region(res->start, resource_size(res)); - return ret; } static struct platform_driver sa1100fb_driver = { diff --git a/drivers/video/fbdev/sa1100fb.h b/drivers/video/fbdev/sa1100fb.h index 0139d13377a5..7a1a9ca33cec 100644 --- a/drivers/video/fbdev/sa1100fb.h +++ b/drivers/video/fbdev/sa1100fb.h @@ -69,6 +69,8 @@ struct sa1100fb_info { const struct sa1100fb_mach_info *inf; struct clk *clk; + + u32 pseudo_palette[16]; }; #define TO_INF(ptr,member) container_of(ptr,struct sa1100fb_info,member) diff --git a/drivers/video/fbdev/sis/init301.c b/drivers/video/fbdev/sis/init301.c index 1ec9c3e0e1d8..02ee752d5000 100644 --- a/drivers/video/fbdev/sis/init301.c +++ b/drivers/video/fbdev/sis/init301.c @@ -6486,7 +6486,7 @@ SiS_SetTVSpecial(struct SiS_Private *SiS_Pr, unsigned short ModeNo) if(!(SiS_Pr->SiS_TVMode & TVSetPAL)) { if(SiS_Pr->SiS_TVMode & TVSetNTSC1024) { - const unsigned char specialtv[] = { + static const unsigned char specialtv[] = { 0xa7,0x07,0xf2,0x6e,0x17,0x8b,0x73,0x53, 0x13,0x40,0x34,0xf4,0x63,0xbb,0xcc,0x7a, 0x58,0xe4,0x73,0xda,0x13 diff --git a/drivers/video/fbdev/sis/sis_main.c b/drivers/video/fbdev/sis/sis_main.c index e92303823a4b..ecdd054d8951 100644 --- a/drivers/video/fbdev/sis/sis_main.c +++ b/drivers/video/fbdev/sis/sis_main.c @@ -1702,6 +1702,7 @@ static int sisfb_ioctl(struct fb_info *info, unsigned int cmd, if(ivideo->warncount++ < 10) printk(KERN_INFO "sisfb: Deprecated ioctl call received - update your application!\n"); + /* fall through */ case SISFB_GET_INFO: /* For communication with X driver */ ivideo->sisfb_infoblock.sisfb_id = SISFB_ID; ivideo->sisfb_infoblock.sisfb_version = VER_MAJOR; @@ -1755,6 +1756,7 @@ static int sisfb_ioctl(struct fb_info *info, unsigned int cmd, if(ivideo->warncount++ < 10) printk(KERN_INFO "sisfb: Deprecated ioctl call received - update your application!\n"); + /* fall through */ case SISFB_GET_VBRSTATUS: if(sisfb_CheckVBRetrace(ivideo)) return put_user((u32)1, argp); @@ -1765,6 +1767,7 @@ static int sisfb_ioctl(struct fb_info *info, unsigned int cmd, if(ivideo->warncount++ < 10) printk(KERN_INFO "sisfb: Deprecated ioctl call received - update your application!\n"); + /* fall through */ case SISFB_GET_AUTOMAXIMIZE: if(ivideo->sisfb_max) return put_user((u32)1, argp); @@ -1775,6 +1778,7 @@ static int sisfb_ioctl(struct fb_info *info, unsigned int cmd, if(ivideo->warncount++ < 10) printk(KERN_INFO "sisfb: Deprecated ioctl call received - update your application!\n"); + /* fall through */ case SISFB_SET_AUTOMAXIMIZE: if(get_user(gpu32, argp)) return -EFAULT; diff --git a/drivers/video/fbdev/sm501fb.c b/drivers/video/fbdev/sm501fb.c index 076dd2711630..6f0a19501c6a 100644 --- a/drivers/video/fbdev/sm501fb.c +++ b/drivers/video/fbdev/sm501fb.c @@ -1008,6 +1008,7 @@ static int sm501fb_blank_crt(int blank_mode, struct fb_info *info) case FB_BLANK_POWERDOWN: ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE; sm501_misc_control(fbi->dev->parent, SM501_MISC_DAC_POWER, 0); + /* fall through */ case FB_BLANK_NORMAL: ctrl |= SM501_DC_CRT_CONTROL_BLANK; @@ -1889,6 +1890,9 @@ static void sm501_free_init_fb(struct sm501fb_info *info, { struct fb_info *fbi = info->fb[head]; + if (!fbi) + return; + fb_dealloc_cmap(&fbi->cmap); } @@ -2076,8 +2080,10 @@ static int sm501fb_remove(struct platform_device *pdev) sm501_free_init_fb(info, HEAD_CRT); sm501_free_init_fb(info, HEAD_PANEL); - unregister_framebuffer(fbinfo_crt); - unregister_framebuffer(fbinfo_pnl); + if (fbinfo_crt) + unregister_framebuffer(fbinfo_crt); + if (fbinfo_pnl) + unregister_framebuffer(fbinfo_pnl); sm501fb_stop(info); kfree(info); @@ -2094,8 +2100,12 @@ static int sm501fb_suspend_fb(struct sm501fb_info *info, enum sm501_controller head) { struct fb_info *fbi = info->fb[head]; - struct sm501fb_par *par = fbi->par; + struct sm501fb_par *par; + + if (!fbi) + return 0; + par = fbi->par; if (par->screen.size == 0) return 0; @@ -2141,8 +2151,12 @@ static void sm501fb_resume_fb(struct sm501fb_info *info, enum sm501_controller head) { struct fb_info *fbi = info->fb[head]; - struct sm501fb_par *par = fbi->par; + struct sm501fb_par *par; + + if (!fbi) + return; + par = fbi->par; if (par->screen.size == 0) return; diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c index ef08a104fb42..d44f14242016 100644 --- a/drivers/video/fbdev/udlfb.c +++ b/drivers/video/fbdev/udlfb.c @@ -769,11 +769,11 @@ static int dlfb_get_edid(struct dlfb_data *dev, char *edid, int len) for (i = 0; i < len; i++) { ret = usb_control_msg(dev->udev, - usb_rcvctrlpipe(dev->udev, 0), (0x02), - (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2, - HZ); - if (ret < 1) { - pr_err("Read EDID byte %d failed err %x\n", i, ret); + usb_rcvctrlpipe(dev->udev, 0), 0x02, + (0x80 | (0x02 << 5)), i << 8, 0xA1, + rbuf, 2, USB_CTRL_GET_TIMEOUT); + if (ret < 2) { + pr_err("Read EDID byte %d failed: %d\n", i, ret); i--; break; } |