diff options
author | David S. Miller <davem@davemloft.net> | 2016-10-14 16:00:27 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-10-14 16:00:27 +0200 |
commit | 8eed1cd4cd0c6f41d40c345265f20dc7688ffcca (patch) | |
tree | 5adaacccdc1e519e3861b8f7333f39350205a472 /drivers | |
parent | Merge branch 's390-net' (diff) | |
parent | Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net (diff) | |
download | linux-8eed1cd4cd0c6f41d40c345265f20dc7688ffcca.tar.xz linux-8eed1cd4cd0c6f41d40c345265f20dc7688ffcca.zip |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
Diffstat (limited to 'drivers')
199 files changed, 8231 insertions, 5459 deletions
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index f4ebe39539af..35e8fbca10ad 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -520,7 +520,8 @@ static void acpi_thermal_check(void *data) if (!tz->tz_enabled) return; - thermal_zone_device_update(tz->thermal_zone); + thermal_zone_device_update(tz->thermal_zone, + THERMAL_EVENT_UNSPECIFIED); } /* sys I/F for generic thermal sysfs support */ diff --git a/drivers/char/tb0219.c b/drivers/char/tb0219.c index 480a777db577..7c19d9b22785 100644 --- a/drivers/char/tb0219.c +++ b/drivers/char/tb0219.c @@ -21,6 +21,7 @@ #include <linux/fs.h> #include <linux/init.h> #include <linux/module.h> +#include <linux/uaccess.h> #include <asm/io.h> #include <asm/reboot.h> diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 3f31ca32f52b..5fa36ebc0640 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -471,9 +471,9 @@ static int bond_check_dev_link(struct bonding *bond, /* Yes, the mii is overlaid on the ifreq.ifr_ifru */ strncpy(ifr.ifr_name, slave_dev->name, IFNAMSIZ); mii = if_mii(&ifr); - if (IOCTL(slave_dev, &ifr, SIOCGMIIPHY) == 0) { + if (ioctl(slave_dev, &ifr, SIOCGMIIPHY) == 0) { mii->reg_num = MII_BMSR; - if (IOCTL(slave_dev, &ifr, SIOCGMIIREG) == 0) + if (ioctl(slave_dev, &ifr, SIOCGMIIREG) == 0) return mii->val_out & BMSR_LSTATUS; } } diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c index bddb198c0b74..380a64115a98 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c @@ -693,7 +693,7 @@ static int cn23xx_enable_io_queues(struct octeon_device *oct) while ((reg_val & CN23XX_PKT_INPUT_CTL_RST) && !(reg_val & CN23XX_PKT_INPUT_CTL_QUIET) && - loop--) { + --loop) { reg_val = octeon_read_csr64( oct, CN23XX_SLI_IQ_PKT_CONTROL64(q_no)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c index d4585154151d..cc4fd61914d3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c @@ -287,7 +287,7 @@ retry: goto retry; } - MLX5_SET64(manage_pages_in, in, pas[i], addr); + MLX5_ARRAY_SET64(manage_pages_in, in, pas, i, addr); } MLX5_SET(manage_pages_in, in, opcode, MLX5_CMD_OP_MANAGE_PAGES); @@ -344,7 +344,7 @@ static int reclaim_pages_cmd(struct mlx5_core_dev *dev, if (fwp->func_id != func_id) continue; - MLX5_SET64(manage_pages_out, out, pas[i], fwp->addr); + MLX5_ARRAY_SET64(manage_pages_out, out, pas, i, fwp->addr); i++; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c index a6db10717d5c..02a8be2faed7 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c @@ -1517,7 +1517,7 @@ static void qed_ll2_register_cb_ops(struct qed_dev *cdev, static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params) { struct qed_ll2_info ll2_info; - struct qed_ll2_buffer *buffer; + struct qed_ll2_buffer *buffer, *tmp_buffer; enum qed_ll2_conn_type conn_type; struct qed_ptt *p_ptt; int rc, i; @@ -1587,7 +1587,7 @@ static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params) /* Post all Rx buffers to FW */ spin_lock_bh(&cdev->ll2->lock); - list_for_each_entry(buffer, &cdev->ll2->list, list) { + list_for_each_entry_safe(buffer, tmp_buffer, &cdev->ll2->list, list) { rc = qed_ll2_post_rx_buffer(QED_LEADING_HWFN(cdev), cdev->ll2->handle, buffer->phys_addr, 0, buffer, 1); diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.c b/drivers/net/ethernet/qlogic/qed/qed_roce.c index 23430059471c..76831a398bed 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_roce.c +++ b/drivers/net/ethernet/qlogic/qed/qed_roce.c @@ -2947,7 +2947,7 @@ static const struct qed_rdma_ops qed_rdma_ops_pass = { .roce_ll2_stats = &qed_roce_ll2_stats, }; -const struct qed_rdma_ops *qed_get_rdma_ops() +const struct qed_rdma_ops *qed_get_rdma_ops(void) { return &qed_rdma_ops_pass; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 1a06c87e3935..da0b80a1917a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -652,20 +652,27 @@ static int stmmac_init_ptp(struct stmmac_priv *priv) if (IS_ERR(priv->clk_ptp_ref)) { priv->clk_ptp_rate = clk_get_rate(priv->stmmac_clk); priv->clk_ptp_ref = NULL; + netdev_dbg(priv->dev, "PTP uses main clock\n"); } else { clk_prepare_enable(priv->clk_ptp_ref); priv->clk_ptp_rate = clk_get_rate(priv->clk_ptp_ref); + netdev_dbg(priv->dev, "PTP rate %d\n", priv->clk_ptp_rate); } priv->adv_ts = 0; - if (priv->dma_cap.atime_stamp && priv->extend_desc) + /* Check if adv_ts can be enabled for dwmac 4.x core */ + if (priv->plat->has_gmac4 && priv->dma_cap.atime_stamp) + priv->adv_ts = 1; + /* Dwmac 3.x core with extend_desc can support adv_ts */ + else if (priv->extend_desc && priv->dma_cap.atime_stamp) priv->adv_ts = 1; - if (netif_msg_hw(priv) && priv->dma_cap.time_stamp) - pr_debug("IEEE 1588-2002 Time Stamp supported\n"); + if (priv->dma_cap.time_stamp) + netdev_info(priv->dev, "IEEE 1588-2002 Timestamp supported\n"); - if (netif_msg_hw(priv) && priv->adv_ts) - pr_debug("IEEE 1588-2008 Advanced Time Stamp supported\n"); + if (priv->adv_ts) + netdev_info(priv->dev, + "IEEE 1588-2008 Advanced Timestamp supported\n"); priv->hw->ptp = &stmmac_ptp; priv->hwts_tx_en = 0; @@ -1702,8 +1709,8 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) if (init_ptp) { ret = stmmac_init_ptp(priv); - if (ret && ret != -EOPNOTSUPP) - pr_warn("%s: failed PTP initialisation\n", __func__); + if (ret) + netdev_warn(priv->dev, "PTP support cannot init.\n"); } #ifdef CONFIG_DEBUG_FS diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c index 6e3b82972ce8..289d52725a6c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c @@ -186,10 +186,12 @@ int stmmac_ptp_register(struct stmmac_priv *priv) priv->device); if (IS_ERR(priv->ptp_clock)) { priv->ptp_clock = NULL; - pr_err("ptp_clock_register() failed on %s\n", priv->dev->name); - } else if (priv->ptp_clock) - pr_debug("Added PTP HW clock successfully on %s\n", - priv->dev->name); + return PTR_ERR(priv->ptp_clock); + } + + spin_lock_init(&priv->ptp_lock); + + netdev_dbg(priv->dev, "Added PTP HW clock successfully\n"); return 0; } diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c index 4a3eeb10d45b..c8d53d8c83ee 100644 --- a/drivers/net/ethernet/ti/tlan.c +++ b/drivers/net/ethernet/ti/tlan.c @@ -610,8 +610,8 @@ err_out_regions: #ifdef CONFIG_PCI if (pdev) pci_release_regions(pdev); -#endif err_out: +#endif if (pdev) pci_disable_device(pdev); return rc; diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 35f9f9742a48..c688d68c39aa 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -431,8 +431,7 @@ static void axienet_setoptions(struct net_device *ndev, u32 options) lp->options |= options; } -static void __axienet_device_reset(struct axienet_local *lp, - struct device *dev, off_t offset) +static void __axienet_device_reset(struct axienet_local *lp, off_t offset) { u32 timeout; /* Reset Axi DMA. This would reset Axi Ethernet core as well. The reset @@ -468,8 +467,8 @@ static void axienet_device_reset(struct net_device *ndev) u32 axienet_status; struct axienet_local *lp = netdev_priv(ndev); - __axienet_device_reset(lp, &ndev->dev, XAXIDMA_TX_CR_OFFSET); - __axienet_device_reset(lp, &ndev->dev, XAXIDMA_RX_CR_OFFSET); + __axienet_device_reset(lp, XAXIDMA_TX_CR_OFFSET); + __axienet_device_reset(lp, XAXIDMA_RX_CR_OFFSET); lp->max_frm_size = XAE_MAX_VLAN_FRAME_SIZE; lp->options |= XAE_OPTION_VLAN; @@ -1338,8 +1337,8 @@ static void axienet_dma_err_handler(unsigned long data) axienet_iow(lp, XAE_MDIO_MC_OFFSET, (mdio_mcreg & ~XAE_MDIO_MC_MDIOEN_MASK)); - __axienet_device_reset(lp, &ndev->dev, XAXIDMA_TX_CR_OFFSET); - __axienet_device_reset(lp, &ndev->dev, XAXIDMA_RX_CR_OFFSET); + __axienet_device_reset(lp, XAXIDMA_TX_CR_OFFSET); + __axienet_device_reset(lp, XAXIDMA_RX_CR_OFFSET); axienet_iow(lp, XAE_MDIO_MC_OFFSET, mdio_mcreg); axienet_mdio_wait_until_ready(lp); diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 52eeb2f67276..f0919bd3a563 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -442,8 +442,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) } net_trans_info = get_net_transport_info(skb, &hdr_offset); - if (net_trans_info == TRANSPORT_INFO_NOT_IP) - goto do_send; /* * Setup the sendside checksum offload only if this is not a @@ -478,56 +476,29 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) } lso_info->lso_v2_transmit.tcp_header_offset = hdr_offset; lso_info->lso_v2_transmit.mss = skb_shinfo(skb)->gso_size; - goto do_send; - } - - if ((skb->ip_summed == CHECKSUM_NONE) || - (skb->ip_summed == CHECKSUM_UNNECESSARY)) - goto do_send; - - rndis_msg_size += NDIS_CSUM_PPI_SIZE; - ppi = init_ppi_data(rndis_msg, NDIS_CSUM_PPI_SIZE, - TCPIP_CHKSUM_PKTINFO); - - csum_info = (struct ndis_tcp_ip_checksum_info *)((void *)ppi + - ppi->ppi_offset); - - if (net_trans_info & (INFO_IPV4 << 16)) - csum_info->transmit.is_ipv4 = 1; - else - csum_info->transmit.is_ipv6 = 1; - - if (net_trans_info & INFO_TCP) { - csum_info->transmit.tcp_checksum = 1; - csum_info->transmit.tcp_header_offset = hdr_offset; - } else if (net_trans_info & INFO_UDP) { - /* UDP checksum offload is not supported on ws2008r2. - * Furthermore, on ws2012 and ws2012r2, there are some - * issues with udp checksum offload from Linux guests. - * (these are host issues). - * For now compute the checksum here. - */ - struct udphdr *uh; - u16 udp_len; - - ret = skb_cow_head(skb, 0); - if (ret) - goto no_memory; - - uh = udp_hdr(skb); - udp_len = ntohs(uh->len); - uh->check = 0; - uh->check = csum_tcpudp_magic(ip_hdr(skb)->saddr, - ip_hdr(skb)->daddr, - udp_len, IPPROTO_UDP, - csum_partial(uh, udp_len, 0)); - if (uh->check == 0) - uh->check = CSUM_MANGLED_0; - - csum_info->transmit.udp_checksum = 0; + } else if (skb->ip_summed == CHECKSUM_PARTIAL) { + if (net_trans_info & INFO_TCP) { + rndis_msg_size += NDIS_CSUM_PPI_SIZE; + ppi = init_ppi_data(rndis_msg, NDIS_CSUM_PPI_SIZE, + TCPIP_CHKSUM_PKTINFO); + + csum_info = (struct ndis_tcp_ip_checksum_info *)((void *)ppi + + ppi->ppi_offset); + + if (net_trans_info & (INFO_IPV4 << 16)) + csum_info->transmit.is_ipv4 = 1; + else + csum_info->transmit.is_ipv6 = 1; + + csum_info->transmit.tcp_checksum = 1; + csum_info->transmit.tcp_header_offset = hdr_offset; + } else { + /* UDP checksum (and other) offload is not supported. */ + if (skb_checksum_help(skb)) + goto drop; + } } -do_send: /* Start filling in the page buffers with the rndis hdr */ rndis_msg->msg_len += rndis_msg_size; packet->total_data_buflen = rndis_msg->msg_len; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index c6f66832a1a6..f424b867f73e 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -608,6 +608,21 @@ void phy_start_machine(struct phy_device *phydev) } /** + * phy_trigger_machine - trigger the state machine to run + * + * @phydev: the phy_device struct + * + * Description: There has been a change in state which requires that the + * state machine runs. + */ + +static void phy_trigger_machine(struct phy_device *phydev) +{ + cancel_delayed_work_sync(&phydev->state_queue); + queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, 0); +} + +/** * phy_stop_machine - stop the PHY state machine tracking * @phydev: target phy_device struct * @@ -639,6 +654,8 @@ static void phy_error(struct phy_device *phydev) mutex_lock(&phydev->lock); phydev->state = PHY_HALTED; mutex_unlock(&phydev->lock); + + phy_trigger_machine(phydev); } /** @@ -800,8 +817,7 @@ void phy_change(struct work_struct *work) } /* reschedule state queue work to run as soon as possible */ - cancel_delayed_work_sync(&phydev->state_queue); - queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, 0); + phy_trigger_machine(phydev); return; ignore: @@ -890,6 +906,8 @@ void phy_start(struct phy_device *phydev) /* if phy was suspended, bring the physical link up again */ if (do_resume) phy_resume(phydev); + + phy_trigger_machine(phydev); } EXPORT_SYMBOL(phy_start); diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 9d1fce8a6e84..3ff76c6db4f6 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -59,6 +59,10 @@ enum qmi_wwan_flags { QMI_WWAN_FLAG_RAWIP = 1 << 0, }; +enum qmi_wwan_quirks { + QMI_WWAN_QUIRK_DTR = 1 << 0, /* needs "set DTR" request */ +}; + static void qmi_wwan_netdev_setup(struct net_device *net) { struct usbnet *dev = netdev_priv(net); @@ -411,9 +415,14 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) * clearing out state the clients might need. * * MDM9x30 is the first QMI chipset with USB3 support. Abuse - * this fact to enable the quirk. + * this fact to enable the quirk for all USB3 devices. + * + * There are also chipsets with the same "set DTR" requirement + * but without USB3 support. Devices based on these chips + * need a quirk flag in the device ID table. */ - if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) { + if (dev->driver_info->data & QMI_WWAN_QUIRK_DTR || + le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) { qmi_wwan_manage_power(dev, 1); qmi_wwan_change_dtr(dev, true); } @@ -526,6 +535,16 @@ static const struct driver_info qmi_wwan_info = { .rx_fixup = qmi_wwan_rx_fixup, }; +static const struct driver_info qmi_wwan_info_quirk_dtr = { + .description = "WWAN/QMI device", + .flags = FLAG_WWAN, + .bind = qmi_wwan_bind, + .unbind = qmi_wwan_unbind, + .manage_power = qmi_wwan_manage_power, + .rx_fixup = qmi_wwan_rx_fixup, + .data = QMI_WWAN_QUIRK_DTR, +}; + #define HUAWEI_VENDOR_ID 0x12D1 /* map QMI/wwan function by a fixed interface number */ @@ -533,6 +552,11 @@ static const struct driver_info qmi_wwan_info = { USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \ .driver_info = (unsigned long)&qmi_wwan_info +/* devices requiring "set DTR" quirk */ +#define QMI_QUIRK_SET_DTR(vend, prod, num) \ + USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \ + .driver_info = (unsigned long)&qmi_wwan_info_quirk_dtr + /* Gobi 1000 QMI/wwan interface number is 3 according to qcserial */ #define QMI_GOBI1K_DEVICE(vend, prod) \ QMI_FIXED_INTF(vend, prod, 3) @@ -895,6 +919,8 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */ {QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */ {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 */ /* 4. Gobi 1000 devices */ {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h index cf68149cbb55..3ce1f7da8647 100644 --- a/drivers/net/xen-netback/common.h +++ b/drivers/net/xen-netback/common.h @@ -407,4 +407,8 @@ u32 xenvif_set_hash_mapping(struct xenvif *vif, u32 gref, u32 len, void xenvif_set_skb_hash(struct xenvif *vif, struct sk_buff *skb); +#ifdef CONFIG_DEBUG_FS +void xenvif_dump_hash_info(struct xenvif *vif, struct seq_file *m); +#endif + #endif /* __XEN_NETBACK__COMMON_H__ */ diff --git a/drivers/net/xen-netback/hash.c b/drivers/net/xen-netback/hash.c index 613bac057650..e8c5dddc54ba 100644 --- a/drivers/net/xen-netback/hash.c +++ b/drivers/net/xen-netback/hash.c @@ -360,6 +360,74 @@ u32 xenvif_set_hash_mapping(struct xenvif *vif, u32 gref, u32 len, return XEN_NETIF_CTRL_STATUS_SUCCESS; } +#ifdef CONFIG_DEBUG_FS +void xenvif_dump_hash_info(struct xenvif *vif, struct seq_file *m) +{ + unsigned int i; + + switch (vif->hash.alg) { + case XEN_NETIF_CTRL_HASH_ALGORITHM_TOEPLITZ: + seq_puts(m, "Hash Algorithm: TOEPLITZ\n"); + break; + + case XEN_NETIF_CTRL_HASH_ALGORITHM_NONE: + seq_puts(m, "Hash Algorithm: NONE\n"); + /* FALLTHRU */ + default: + return; + } + + if (vif->hash.flags) { + seq_puts(m, "\nHash Flags:\n"); + + if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV4) + seq_puts(m, "- IPv4\n"); + if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP) + seq_puts(m, "- IPv4 + TCP\n"); + if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV6) + seq_puts(m, "- IPv6\n"); + if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP) + seq_puts(m, "- IPv6 + TCP\n"); + } + + seq_puts(m, "\nHash Key:\n"); + + for (i = 0; i < XEN_NETBK_MAX_HASH_KEY_SIZE; ) { + unsigned int j, n; + + n = 8; + if (i + n >= XEN_NETBK_MAX_HASH_KEY_SIZE) + n = XEN_NETBK_MAX_HASH_KEY_SIZE - i; + + seq_printf(m, "[%2u - %2u]: ", i, i + n - 1); + + for (j = 0; j < n; j++, i++) + seq_printf(m, "%02x ", vif->hash.key[i]); + + seq_puts(m, "\n"); + } + + if (vif->hash.size != 0) { + seq_puts(m, "\nHash Mapping:\n"); + + for (i = 0; i < vif->hash.size; ) { + unsigned int j, n; + + n = 8; + if (i + n >= vif->hash.size) + n = vif->hash.size - i; + + seq_printf(m, "[%4u - %4u]: ", i, i + n - 1); + + for (j = 0; j < n; j++, i++) + seq_printf(m, "%4u ", vif->hash.mapping[i]); + + seq_puts(m, "\n"); + } + } +} +#endif /* CONFIG_DEBUG_FS */ + void xenvif_init_hash(struct xenvif *vif) { if (xenvif_hash_cache_size == 0) diff --git a/drivers/net/xen-netback/rx.c b/drivers/net/xen-netback/rx.c index 8e9ade6ccf18..b1cf7c6f407a 100644 --- a/drivers/net/xen-netback/rx.c +++ b/drivers/net/xen-netback/rx.c @@ -337,9 +337,9 @@ static void xenvif_rx_next_chunk(struct xenvif_queue *queue, frag_data += pkt->frag_offset; frag_len -= pkt->frag_offset; - chunk_len = min(frag_len, XEN_PAGE_SIZE - offset); - chunk_len = min(chunk_len, - XEN_PAGE_SIZE - xen_offset_in_page(frag_data)); + chunk_len = min_t(size_t, frag_len, XEN_PAGE_SIZE - offset); + chunk_len = min_t(size_t, chunk_len, XEN_PAGE_SIZE - + xen_offset_in_page(frag_data)); pkt->frag_offset += chunk_len; @@ -425,6 +425,8 @@ void xenvif_rx_skb(struct xenvif_queue *queue) xenvif_rx_next_skb(queue, &pkt); + queue->last_rx_time = jiffies; + do { struct xen_netif_rx_request *req; struct xen_netif_rx_response *rsp; diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 7056404e3cb8..8674e188b697 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -165,7 +165,7 @@ xenvif_write_io_ring(struct file *filp, const char __user *buf, size_t count, return count; } -static int xenvif_dump_open(struct inode *inode, struct file *filp) +static int xenvif_io_ring_open(struct inode *inode, struct file *filp) { int ret; void *queue = NULL; @@ -179,13 +179,35 @@ static int xenvif_dump_open(struct inode *inode, struct file *filp) static const struct file_operations xenvif_dbg_io_ring_ops_fops = { .owner = THIS_MODULE, - .open = xenvif_dump_open, + .open = xenvif_io_ring_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .write = xenvif_write_io_ring, }; +static int xenvif_read_ctrl(struct seq_file *m, void *v) +{ + struct xenvif *vif = m->private; + + xenvif_dump_hash_info(vif, m); + + return 0; +} + +static int xenvif_ctrl_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, xenvif_read_ctrl, inode->i_private); +} + +static const struct file_operations xenvif_dbg_ctrl_ops_fops = { + .owner = THIS_MODULE, + .open = xenvif_ctrl_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static void xenvif_debugfs_addif(struct xenvif *vif) { struct dentry *pfile; @@ -210,6 +232,17 @@ static void xenvif_debugfs_addif(struct xenvif *vif) pr_warn("Creation of io_ring file returned %ld!\n", PTR_ERR(pfile)); } + + if (vif->ctrl_irq) { + pfile = debugfs_create_file("ctrl", + S_IRUSR, + vif->xenvif_dbg_root, + vif, + &xenvif_dbg_ctrl_ops_fops); + if (IS_ERR_OR_NULL(pfile)) + pr_warn("Creation of ctrl file returned %ld!\n", + PTR_ERR(pfile)); + } } else netdev_warn(vif->dev, "Creation of vif debugfs dir returned %ld!\n", diff --git a/drivers/pci/host/pci-aardvark.c b/drivers/pci/host/pci-aardvark.c index e4a5b7ee90cf..4fce494271cc 100644 --- a/drivers/pci/host/pci-aardvark.c +++ b/drivers/pci/host/pci-aardvark.c @@ -230,20 +230,20 @@ static int advk_pcie_link_up(struct advk_pcie *pcie) static int advk_pcie_wait_for_link(struct advk_pcie *pcie) { + struct device *dev = &pcie->pdev->dev; int retries; /* check if the link is up or not */ for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { if (advk_pcie_link_up(pcie)) { - dev_info(&pcie->pdev->dev, "link up\n"); + dev_info(dev, "link up\n"); return 0; } usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); } - dev_err(&pcie->pdev->dev, "link never came up\n"); - + dev_err(dev, "link never came up\n"); return -ETIMEDOUT; } @@ -376,6 +376,7 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) static void advk_pcie_check_pio_status(struct advk_pcie *pcie) { + struct device *dev = &pcie->pdev->dev; u32 reg; unsigned int status; char *strcomp_status, *str_posted; @@ -407,12 +408,13 @@ static void advk_pcie_check_pio_status(struct advk_pcie *pcie) else str_posted = "Posted"; - dev_err(&pcie->pdev->dev, "%s PIO Response Status: %s, %#x @ %#x\n", + dev_err(dev, "%s PIO Response Status: %s, %#x @ %#x\n", str_posted, strcomp_status, reg, advk_readl(pcie, PIO_ADDR_LS)); } static int advk_pcie_wait_pio(struct advk_pcie *pcie) { + struct device *dev = &pcie->pdev->dev; unsigned long timeout; timeout = jiffies + msecs_to_jiffies(PIO_TIMEOUT_MS); @@ -426,7 +428,7 @@ static int advk_pcie_wait_pio(struct advk_pcie *pcie) return 0; } - dev_err(&pcie->pdev->dev, "config read/write timed out\n"); + dev_err(dev, "config read/write timed out\n"); return -ETIMEDOUT; } @@ -560,10 +562,11 @@ static int advk_pcie_alloc_msi(struct advk_pcie *pcie) static void advk_pcie_free_msi(struct advk_pcie *pcie, int hwirq) { + struct device *dev = &pcie->pdev->dev; + mutex_lock(&pcie->msi_used_lock); if (!test_bit(hwirq, pcie->msi_irq_in_use)) - dev_err(&pcie->pdev->dev, "trying to free unused MSI#%d\n", - hwirq); + dev_err(dev, "trying to free unused MSI#%d\n", hwirq); else clear_bit(hwirq, pcie->msi_irq_in_use); mutex_unlock(&pcie->msi_used_lock); @@ -910,6 +913,7 @@ out_release_res: static int advk_pcie_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct advk_pcie *pcie; struct resource *res; struct pci_bus *bus, *child; @@ -917,31 +921,29 @@ static int advk_pcie_probe(struct platform_device *pdev) struct device_node *msi_node; int ret, irq; - pcie = devm_kzalloc(&pdev->dev, sizeof(struct advk_pcie), - GFP_KERNEL); + pcie = devm_kzalloc(dev, sizeof(struct advk_pcie), GFP_KERNEL); if (!pcie) return -ENOMEM; pcie->pdev = pdev; - platform_set_drvdata(pdev, pcie); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pcie->base = devm_ioremap_resource(&pdev->dev, res); + pcie->base = devm_ioremap_resource(dev, res); if (IS_ERR(pcie->base)) return PTR_ERR(pcie->base); irq = platform_get_irq(pdev, 0); - ret = devm_request_irq(&pdev->dev, irq, advk_pcie_irq_handler, + ret = devm_request_irq(dev, irq, advk_pcie_irq_handler, IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie", pcie); if (ret) { - dev_err(&pdev->dev, "Failed to register interrupt\n"); + dev_err(dev, "Failed to register interrupt\n"); return ret; } ret = advk_pcie_parse_request_of_pci_ranges(pcie); if (ret) { - dev_err(&pdev->dev, "Failed to parse resources\n"); + dev_err(dev, "Failed to parse resources\n"); return ret; } @@ -949,24 +951,24 @@ static int advk_pcie_probe(struct platform_device *pdev) ret = advk_pcie_init_irq_domain(pcie); if (ret) { - dev_err(&pdev->dev, "Failed to initialize irq\n"); + dev_err(dev, "Failed to initialize irq\n"); return ret; } ret = advk_pcie_init_msi_irq_domain(pcie); if (ret) { - dev_err(&pdev->dev, "Failed to initialize irq\n"); + dev_err(dev, "Failed to initialize irq\n"); advk_pcie_remove_irq_domain(pcie); return ret; } - msi_node = of_parse_phandle(pdev->dev.of_node, "msi-parent", 0); + msi_node = of_parse_phandle(dev->of_node, "msi-parent", 0); if (msi_node) msi = of_pci_find_msi_chip_by_node(msi_node); else msi = NULL; - bus = pci_scan_root_bus_msi(&pdev->dev, 0, &advk_pcie_ops, + bus = pci_scan_root_bus_msi(dev, 0, &advk_pcie_ops, pcie, &pcie->resources, &pcie->msi); if (!bus) { advk_pcie_remove_msi_irq_domain(pcie); @@ -980,7 +982,6 @@ static int advk_pcie_probe(struct platform_device *pdev) pcie_bus_configure_settings(child); pci_bus_add_devices(bus); - return 0; } diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/host/pci-dra7xx.c index 19223ed2e619..9595fad63f6f 100644 --- a/drivers/pci/host/pci-dra7xx.c +++ b/drivers/pci/host/pci-dra7xx.c @@ -64,11 +64,10 @@ #define DRA7XX_CPU_TO_BUS_ADDR 0x0FFFFFFF struct dra7xx_pcie { - void __iomem *base; - struct phy **phy; - int phy_count; - struct device *dev; struct pcie_port pp; + void __iomem *base; /* DT ti_conf */ + int phy_count; /* DT phy-names count */ + struct phy **phy; }; #define to_dra7xx_pcie(x) container_of((x), struct dra7xx_pcie, pp) @@ -84,17 +83,6 @@ static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset, writel(value, pcie->base + offset); } -static inline u32 dra7xx_pcie_readl_rc(struct pcie_port *pp, u32 offset) -{ - return readl(pp->dbi_base + offset); -} - -static inline void dra7xx_pcie_writel_rc(struct pcie_port *pp, u32 offset, - u32 value) -{ - writel(value, pp->dbi_base + offset); -} - static int dra7xx_pcie_link_up(struct pcie_port *pp) { struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); @@ -103,13 +91,14 @@ static int dra7xx_pcie_link_up(struct pcie_port *pp) return !!(reg & LINK_UP); } -static int dra7xx_pcie_establish_link(struct pcie_port *pp) +static int dra7xx_pcie_establish_link(struct dra7xx_pcie *dra7xx) { - struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); + struct pcie_port *pp = &dra7xx->pp; + struct device *dev = pp->dev; u32 reg; if (dw_pcie_link_up(pp)) { - dev_err(pp->dev, "link is already up\n"); + dev_err(dev, "link is already up\n"); return 0; } @@ -120,10 +109,8 @@ static int dra7xx_pcie_establish_link(struct pcie_port *pp) return dw_pcie_wait_for_link(pp); } -static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp) +static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx) { - struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); - dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, ~INTERRUPTS); dra7xx_pcie_writel(dra7xx, @@ -142,6 +129,8 @@ static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp) static void dra7xx_pcie_host_init(struct pcie_port *pp) { + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); + pp->io_base &= DRA7XX_CPU_TO_BUS_ADDR; pp->mem_base &= DRA7XX_CPU_TO_BUS_ADDR; pp->cfg0_base &= DRA7XX_CPU_TO_BUS_ADDR; @@ -149,10 +138,10 @@ static void dra7xx_pcie_host_init(struct pcie_port *pp) dw_pcie_setup_rc(pp); - dra7xx_pcie_establish_link(pp); + dra7xx_pcie_establish_link(dra7xx); if (IS_ENABLED(CONFIG_PCI_MSI)) dw_pcie_msi_init(pp); - dra7xx_pcie_enable_interrupts(pp); + dra7xx_pcie_enable_interrupts(dra7xx); } static struct pcie_host_ops dra7xx_pcie_host_ops = { @@ -196,8 +185,8 @@ static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp) static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg) { - struct pcie_port *pp = arg; - struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); + struct dra7xx_pcie *dra7xx = arg; + struct pcie_port *pp = &dra7xx->pp; u32 reg; reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI); @@ -223,51 +212,51 @@ static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg) static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg) { struct dra7xx_pcie *dra7xx = arg; + struct device *dev = dra7xx->pp.dev; u32 reg; reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN); if (reg & ERR_SYS) - dev_dbg(dra7xx->dev, "System Error\n"); + dev_dbg(dev, "System Error\n"); if (reg & ERR_FATAL) - dev_dbg(dra7xx->dev, "Fatal Error\n"); + dev_dbg(dev, "Fatal Error\n"); if (reg & ERR_NONFATAL) - dev_dbg(dra7xx->dev, "Non Fatal Error\n"); + dev_dbg(dev, "Non Fatal Error\n"); if (reg & ERR_COR) - dev_dbg(dra7xx->dev, "Correctable Error\n"); + dev_dbg(dev, "Correctable Error\n"); if (reg & ERR_AXI) - dev_dbg(dra7xx->dev, "AXI tag lookup fatal Error\n"); + dev_dbg(dev, "AXI tag lookup fatal Error\n"); if (reg & ERR_ECRC) - dev_dbg(dra7xx->dev, "ECRC Error\n"); + dev_dbg(dev, "ECRC Error\n"); if (reg & PME_TURN_OFF) - dev_dbg(dra7xx->dev, + dev_dbg(dev, "Power Management Event Turn-Off message received\n"); if (reg & PME_TO_ACK) - dev_dbg(dra7xx->dev, + dev_dbg(dev, "Power Management Turn-Off Ack message received\n"); if (reg & PM_PME) - dev_dbg(dra7xx->dev, - "PM Power Management Event message received\n"); + dev_dbg(dev, "PM Power Management Event message received\n"); if (reg & LINK_REQ_RST) - dev_dbg(dra7xx->dev, "Link Request Reset\n"); + dev_dbg(dev, "Link Request Reset\n"); if (reg & LINK_UP_EVT) - dev_dbg(dra7xx->dev, "Link-up state change\n"); + dev_dbg(dev, "Link-up state change\n"); if (reg & CFG_BME_EVT) - dev_dbg(dra7xx->dev, "CFG 'Bus Master Enable' change\n"); + dev_dbg(dev, "CFG 'Bus Master Enable' change\n"); if (reg & CFG_MSE_EVT) - dev_dbg(dra7xx->dev, "CFG 'Memory Space Enable' change\n"); + dev_dbg(dev, "CFG 'Memory Space Enable' change\n"); dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, reg); @@ -278,13 +267,9 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx, struct platform_device *pdev) { int ret; - struct pcie_port *pp; + struct pcie_port *pp = &dra7xx->pp; + struct device *dev = pp->dev; struct resource *res; - struct device *dev = &pdev->dev; - - pp = &dra7xx->pp; - pp->dev = dev; - pp->ops = &dra7xx_pcie_host_ops; pp->irq = platform_get_irq(pdev, 1); if (pp->irq < 0) { @@ -292,12 +277,11 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx, return -EINVAL; } - ret = devm_request_irq(&pdev->dev, pp->irq, - dra7xx_pcie_msi_irq_handler, + ret = devm_request_irq(dev, pp->irq, dra7xx_pcie_msi_irq_handler, IRQF_SHARED | IRQF_NO_THREAD, - "dra7-pcie-msi", pp); + "dra7-pcie-msi", dra7xx); if (ret) { - dev_err(&pdev->dev, "failed to request irq\n"); + dev_err(dev, "failed to request irq\n"); return ret; } @@ -314,7 +298,7 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx, ret = dw_pcie_host_init(pp); if (ret) { - dev_err(dra7xx->dev, "failed to initialize host\n"); + dev_err(dev, "failed to initialize host\n"); return ret; } @@ -332,6 +316,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) void __iomem *base; struct resource *res; struct dra7xx_pcie *dra7xx; + struct pcie_port *pp; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; char name[10]; @@ -343,6 +328,10 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) if (!dra7xx) return -ENOMEM; + pp = &dra7xx->pp; + pp->dev = dev; + pp->ops = &dra7xx_pcie_host_ops; + irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "missing IRQ resource\n"); @@ -390,7 +379,6 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) dra7xx->base = base; dra7xx->phy = phy; - dra7xx->dev = dev; dra7xx->phy_count = phy_count; pm_runtime_enable(dev); @@ -407,7 +395,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) ret = devm_gpio_request_one(dev, gpio_sel, gpio_flags, "pcie_reset"); if (ret) { - dev_err(&pdev->dev, "gpio%d request failed, ret %d\n", + dev_err(dev, "gpio%d request failed, ret %d\n", gpio_sel, ret); goto err_gpio; } @@ -420,12 +408,11 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev) reg &= ~LTSSM_EN; dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); - platform_set_drvdata(pdev, dra7xx); - ret = dra7xx_add_pcie_port(dra7xx, pdev); if (ret < 0) goto err_gpio; + platform_set_drvdata(pdev, dra7xx); return 0; err_gpio: @@ -451,9 +438,9 @@ static int dra7xx_pcie_suspend(struct device *dev) u32 val; /* clear MSE */ - val = dra7xx_pcie_readl_rc(pp, PCI_COMMAND); + val = dw_pcie_readl_rc(pp, PCI_COMMAND); val &= ~PCI_COMMAND_MEMORY; - dra7xx_pcie_writel_rc(pp, PCI_COMMAND, val); + dw_pcie_writel_rc(pp, PCI_COMMAND, val); return 0; } @@ -465,9 +452,9 @@ static int dra7xx_pcie_resume(struct device *dev) u32 val; /* set MSE */ - val = dra7xx_pcie_readl_rc(pp, PCI_COMMAND); + val = dw_pcie_readl_rc(pp, PCI_COMMAND); val |= PCI_COMMAND_MEMORY; - dra7xx_pcie_writel_rc(pp, PCI_COMMAND, val); + dw_pcie_writel_rc(pp, PCI_COMMAND, val); return 0; } diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c index 2e2d7f00b9e8..f1c544bb8b68 100644 --- a/drivers/pci/host/pci-exynos.c +++ b/drivers/pci/host/pci-exynos.c @@ -29,13 +29,13 @@ #define to_exynos_pcie(x) container_of(x, struct exynos_pcie, pp) struct exynos_pcie { - void __iomem *elbi_base; - void __iomem *phy_base; - void __iomem *block_base; + struct pcie_port pp; + void __iomem *elbi_base; /* DT 0th resource */ + void __iomem *phy_base; /* DT 1st resource */ + void __iomem *block_base; /* DT 2nd resource */ int reset_gpio; struct clk *clk; struct clk *bus_clk; - struct pcie_port pp; }; /* PCIe ELBI registers */ @@ -102,40 +102,40 @@ struct exynos_pcie { #define PCIE_PHY_TRSV3_PD_TSV (0x1 << 7) #define PCIE_PHY_TRSV3_LVCC 0x31c -static inline void exynos_elb_writel(struct exynos_pcie *pcie, u32 val, u32 reg) +static void exynos_elb_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg) { - writel(val, pcie->elbi_base + reg); + writel(val, exynos_pcie->elbi_base + reg); } -static inline u32 exynos_elb_readl(struct exynos_pcie *pcie, u32 reg) +static u32 exynos_elb_readl(struct exynos_pcie *exynos_pcie, u32 reg) { - return readl(pcie->elbi_base + reg); + return readl(exynos_pcie->elbi_base + reg); } -static inline void exynos_phy_writel(struct exynos_pcie *pcie, u32 val, u32 reg) +static void exynos_phy_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg) { - writel(val, pcie->phy_base + reg); + writel(val, exynos_pcie->phy_base + reg); } -static inline u32 exynos_phy_readl(struct exynos_pcie *pcie, u32 reg) +static u32 exynos_phy_readl(struct exynos_pcie *exynos_pcie, u32 reg) { - return readl(pcie->phy_base + reg); + return readl(exynos_pcie->phy_base + reg); } -static inline void exynos_blk_writel(struct exynos_pcie *pcie, u32 val, u32 reg) +static void exynos_blk_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg) { - writel(val, pcie->block_base + reg); + writel(val, exynos_pcie->block_base + reg); } -static inline u32 exynos_blk_readl(struct exynos_pcie *pcie, u32 reg) +static u32 exynos_blk_readl(struct exynos_pcie *exynos_pcie, u32 reg) { - return readl(pcie->block_base + reg); + return readl(exynos_pcie->block_base + reg); } -static void exynos_pcie_sideband_dbi_w_mode(struct pcie_port *pp, bool on) +static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *exynos_pcie, + bool on) { u32 val; - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); if (on) { val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_AWMISC); @@ -148,10 +148,10 @@ static void exynos_pcie_sideband_dbi_w_mode(struct pcie_port *pp, bool on) } } -static void exynos_pcie_sideband_dbi_r_mode(struct pcie_port *pp, bool on) +static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *exynos_pcie, + bool on) { u32 val; - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); if (on) { val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_ARMISC); @@ -164,10 +164,9 @@ static void exynos_pcie_sideband_dbi_r_mode(struct pcie_port *pp, bool on) } } -static void exynos_pcie_assert_core_reset(struct pcie_port *pp) +static void exynos_pcie_assert_core_reset(struct exynos_pcie *exynos_pcie) { u32 val; - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); val = exynos_elb_readl(exynos_pcie, PCIE_CORE_RESET); val &= ~PCIE_CORE_RESET_ENABLE; @@ -177,10 +176,9 @@ static void exynos_pcie_assert_core_reset(struct pcie_port *pp) exynos_elb_writel(exynos_pcie, 0, PCIE_NONSTICKY_RESET); } -static void exynos_pcie_deassert_core_reset(struct pcie_port *pp) +static void exynos_pcie_deassert_core_reset(struct exynos_pcie *exynos_pcie) { u32 val; - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); val = exynos_elb_readl(exynos_pcie, PCIE_CORE_RESET); val |= PCIE_CORE_RESET_ENABLE; @@ -193,18 +191,14 @@ static void exynos_pcie_deassert_core_reset(struct pcie_port *pp) exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_MAC_RESET); } -static void exynos_pcie_assert_phy_reset(struct pcie_port *pp) +static void exynos_pcie_assert_phy_reset(struct exynos_pcie *exynos_pcie) { - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); - exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_MAC_RESET); exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_GLOBAL_RESET); } -static void exynos_pcie_deassert_phy_reset(struct pcie_port *pp) +static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *exynos_pcie) { - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); - exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_GLOBAL_RESET); exynos_elb_writel(exynos_pcie, 1, PCIE_PWR_RESET); exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET); @@ -213,10 +207,9 @@ static void exynos_pcie_deassert_phy_reset(struct pcie_port *pp) exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSV_RESET); } -static void exynos_pcie_power_on_phy(struct pcie_port *pp) +static void exynos_pcie_power_on_phy(struct exynos_pcie *exynos_pcie) { u32 val; - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER); val &= ~PCIE_PHY_COMMON_PD_CMN; @@ -239,10 +232,9 @@ static void exynos_pcie_power_on_phy(struct pcie_port *pp) exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER); } -static void exynos_pcie_power_off_phy(struct pcie_port *pp) +static void exynos_pcie_power_off_phy(struct exynos_pcie *exynos_pcie) { u32 val; - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER); val |= PCIE_PHY_COMMON_PD_CMN; @@ -265,10 +257,8 @@ static void exynos_pcie_power_off_phy(struct pcie_port *pp) exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER); } -static void exynos_pcie_init_phy(struct pcie_port *pp) +static void exynos_pcie_init_phy(struct exynos_pcie *exynos_pcie) { - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); - /* DCC feedback control off */ exynos_phy_writel(exynos_pcie, 0x29, PCIE_PHY_DCC_FEEDBACK); @@ -305,51 +295,41 @@ static void exynos_pcie_init_phy(struct pcie_port *pp) exynos_phy_writel(exynos_pcie, 0xa0, PCIE_PHY_TRSV3_LVCC); } -static void exynos_pcie_assert_reset(struct pcie_port *pp) +static void exynos_pcie_assert_reset(struct exynos_pcie *exynos_pcie) { - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); + struct pcie_port *pp = &exynos_pcie->pp; + struct device *dev = pp->dev; if (exynos_pcie->reset_gpio >= 0) - devm_gpio_request_one(pp->dev, exynos_pcie->reset_gpio, + devm_gpio_request_one(dev, exynos_pcie->reset_gpio, GPIOF_OUT_INIT_HIGH, "RESET"); } -static int exynos_pcie_establish_link(struct pcie_port *pp) +static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie) { - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); + struct pcie_port *pp = &exynos_pcie->pp; + struct device *dev = pp->dev; u32 val; if (dw_pcie_link_up(pp)) { - dev_err(pp->dev, "Link already up\n"); + dev_err(dev, "Link already up\n"); return 0; } - /* assert reset signals */ - exynos_pcie_assert_core_reset(pp); - exynos_pcie_assert_phy_reset(pp); - - /* de-assert phy reset */ - exynos_pcie_deassert_phy_reset(pp); - - /* power on phy */ - exynos_pcie_power_on_phy(pp); - - /* initialize phy */ - exynos_pcie_init_phy(pp); + exynos_pcie_assert_core_reset(exynos_pcie); + exynos_pcie_assert_phy_reset(exynos_pcie); + exynos_pcie_deassert_phy_reset(exynos_pcie); + exynos_pcie_power_on_phy(exynos_pcie); + exynos_pcie_init_phy(exynos_pcie); /* pulse for common reset */ exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_COMMON_RESET); udelay(500); exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET); - /* de-assert core reset */ - exynos_pcie_deassert_core_reset(pp); - - /* setup root complex */ + exynos_pcie_deassert_core_reset(exynos_pcie); dw_pcie_setup_rc(pp); - - /* assert reset signal */ - exynos_pcie_assert_reset(pp); + exynos_pcie_assert_reset(exynos_pcie); /* assert LTSSM enable */ exynos_elb_writel(exynos_pcie, PCIE_ELBI_LTSSM_ENABLE, @@ -361,27 +341,23 @@ static int exynos_pcie_establish_link(struct pcie_port *pp) while (exynos_phy_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED) == 0) { val = exynos_blk_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED); - dev_info(pp->dev, "PLL Locked: 0x%x\n", val); + dev_info(dev, "PLL Locked: 0x%x\n", val); } - /* power off phy */ - exynos_pcie_power_off_phy(pp); - + exynos_pcie_power_off_phy(exynos_pcie); return -ETIMEDOUT; } -static void exynos_pcie_clear_irq_pulse(struct pcie_port *pp) +static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *exynos_pcie) { u32 val; - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); val = exynos_elb_readl(exynos_pcie, PCIE_IRQ_PULSE); exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_PULSE); } -static void exynos_pcie_enable_irq_pulse(struct pcie_port *pp) +static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *exynos_pcie) { u32 val; - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); /* enable INTX interrupt */ val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT | @@ -391,23 +367,24 @@ static void exynos_pcie_enable_irq_pulse(struct pcie_port *pp) static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) { - struct pcie_port *pp = arg; + struct exynos_pcie *exynos_pcie = arg; - exynos_pcie_clear_irq_pulse(pp); + exynos_pcie_clear_irq_pulse(exynos_pcie); return IRQ_HANDLED; } static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg) { - struct pcie_port *pp = arg; + struct exynos_pcie *exynos_pcie = arg; + struct pcie_port *pp = &exynos_pcie->pp; return dw_handle_msi_irq(pp); } -static void exynos_pcie_msi_init(struct pcie_port *pp) +static void exynos_pcie_msi_init(struct exynos_pcie *exynos_pcie) { + struct pcie_port *pp = &exynos_pcie->pp; u32 val; - struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); dw_pcie_msi_init(pp); @@ -417,60 +394,64 @@ static void exynos_pcie_msi_init(struct pcie_port *pp) exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_LEVEL); } -static void exynos_pcie_enable_interrupts(struct pcie_port *pp) +static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie) { - exynos_pcie_enable_irq_pulse(pp); + exynos_pcie_enable_irq_pulse(exynos_pcie); if (IS_ENABLED(CONFIG_PCI_MSI)) - exynos_pcie_msi_init(pp); + exynos_pcie_msi_init(exynos_pcie); } -static inline u32 exynos_pcie_readl_rc(struct pcie_port *pp, - void __iomem *dbi_base) +static u32 exynos_pcie_readl_rc(struct pcie_port *pp, u32 reg) { + struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); u32 val; - exynos_pcie_sideband_dbi_r_mode(pp, true); - val = readl(dbi_base); - exynos_pcie_sideband_dbi_r_mode(pp, false); + exynos_pcie_sideband_dbi_r_mode(exynos_pcie, true); + val = readl(pp->dbi_base + reg); + exynos_pcie_sideband_dbi_r_mode(exynos_pcie, false); return val; } -static inline void exynos_pcie_writel_rc(struct pcie_port *pp, - u32 val, void __iomem *dbi_base) +static void exynos_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val) { - exynos_pcie_sideband_dbi_w_mode(pp, true); - writel(val, dbi_base); - exynos_pcie_sideband_dbi_w_mode(pp, false); + struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); + + exynos_pcie_sideband_dbi_w_mode(exynos_pcie, true); + writel(val, pp->dbi_base + reg); + exynos_pcie_sideband_dbi_w_mode(exynos_pcie, false); } static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val) { + struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); int ret; - exynos_pcie_sideband_dbi_r_mode(pp, true); + exynos_pcie_sideband_dbi_r_mode(exynos_pcie, true); ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val); - exynos_pcie_sideband_dbi_r_mode(pp, false); + exynos_pcie_sideband_dbi_r_mode(exynos_pcie, false); return ret; } static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, u32 val) { + struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); int ret; - exynos_pcie_sideband_dbi_w_mode(pp, true); + exynos_pcie_sideband_dbi_w_mode(exynos_pcie, true); ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val); - exynos_pcie_sideband_dbi_w_mode(pp, false); + exynos_pcie_sideband_dbi_w_mode(exynos_pcie, false); return ret; } static int exynos_pcie_link_up(struct pcie_port *pp) { struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); - u32 val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_RDLH_LINKUP); + u32 val; + val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_RDLH_LINKUP); if (val == PCIE_ELBI_LTSSM_ENABLE) return 1; @@ -479,8 +460,10 @@ static int exynos_pcie_link_up(struct pcie_port *pp) static void exynos_pcie_host_init(struct pcie_port *pp) { - exynos_pcie_establish_link(pp); - exynos_pcie_enable_interrupts(pp); + struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); + + exynos_pcie_establish_link(exynos_pcie); + exynos_pcie_enable_interrupts(exynos_pcie); } static struct pcie_host_ops exynos_pcie_host_ops = { @@ -492,36 +475,38 @@ static struct pcie_host_ops exynos_pcie_host_ops = { .host_init = exynos_pcie_host_init, }; -static int __init exynos_add_pcie_port(struct pcie_port *pp, +static int __init exynos_add_pcie_port(struct exynos_pcie *exynos_pcie, struct platform_device *pdev) { + struct pcie_port *pp = &exynos_pcie->pp; + struct device *dev = pp->dev; int ret; pp->irq = platform_get_irq(pdev, 1); if (!pp->irq) { - dev_err(&pdev->dev, "failed to get irq\n"); + dev_err(dev, "failed to get irq\n"); return -ENODEV; } - ret = devm_request_irq(&pdev->dev, pp->irq, exynos_pcie_irq_handler, - IRQF_SHARED, "exynos-pcie", pp); + ret = devm_request_irq(dev, pp->irq, exynos_pcie_irq_handler, + IRQF_SHARED, "exynos-pcie", exynos_pcie); if (ret) { - dev_err(&pdev->dev, "failed to request irq\n"); + dev_err(dev, "failed to request irq\n"); return ret; } if (IS_ENABLED(CONFIG_PCI_MSI)) { pp->msi_irq = platform_get_irq(pdev, 0); if (!pp->msi_irq) { - dev_err(&pdev->dev, "failed to get msi irq\n"); + dev_err(dev, "failed to get msi irq\n"); return -ENODEV; } - ret = devm_request_irq(&pdev->dev, pp->msi_irq, + ret = devm_request_irq(dev, pp->msi_irq, exynos_pcie_msi_irq_handler, IRQF_SHARED | IRQF_NO_THREAD, - "exynos-pcie", pp); + "exynos-pcie", exynos_pcie); if (ret) { - dev_err(&pdev->dev, "failed to request msi irq\n"); + dev_err(dev, "failed to request msi irq\n"); return ret; } } @@ -531,7 +516,7 @@ static int __init exynos_add_pcie_port(struct pcie_port *pp, ret = dw_pcie_host_init(pp); if (ret) { - dev_err(&pdev->dev, "failed to initialize host\n"); + dev_err(dev, "failed to initialize host\n"); return ret; } @@ -540,37 +525,36 @@ static int __init exynos_add_pcie_port(struct pcie_port *pp, static int __init exynos_pcie_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct exynos_pcie *exynos_pcie; struct pcie_port *pp; - struct device_node *np = pdev->dev.of_node; + struct device_node *np = dev->of_node; struct resource *elbi_base; struct resource *phy_base; struct resource *block_base; int ret; - exynos_pcie = devm_kzalloc(&pdev->dev, sizeof(*exynos_pcie), - GFP_KERNEL); + exynos_pcie = devm_kzalloc(dev, sizeof(*exynos_pcie), GFP_KERNEL); if (!exynos_pcie) return -ENOMEM; pp = &exynos_pcie->pp; - - pp->dev = &pdev->dev; + pp->dev = dev; exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); - exynos_pcie->clk = devm_clk_get(&pdev->dev, "pcie"); + exynos_pcie->clk = devm_clk_get(dev, "pcie"); if (IS_ERR(exynos_pcie->clk)) { - dev_err(&pdev->dev, "Failed to get pcie rc clock\n"); + dev_err(dev, "Failed to get pcie rc clock\n"); return PTR_ERR(exynos_pcie->clk); } ret = clk_prepare_enable(exynos_pcie->clk); if (ret) return ret; - exynos_pcie->bus_clk = devm_clk_get(&pdev->dev, "pcie_bus"); + exynos_pcie->bus_clk = devm_clk_get(dev, "pcie_bus"); if (IS_ERR(exynos_pcie->bus_clk)) { - dev_err(&pdev->dev, "Failed to get pcie bus clock\n"); + dev_err(dev, "Failed to get pcie bus clock\n"); ret = PTR_ERR(exynos_pcie->bus_clk); goto fail_clk; } @@ -579,27 +563,27 @@ static int __init exynos_pcie_probe(struct platform_device *pdev) goto fail_clk; elbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); - exynos_pcie->elbi_base = devm_ioremap_resource(&pdev->dev, elbi_base); + exynos_pcie->elbi_base = devm_ioremap_resource(dev, elbi_base); if (IS_ERR(exynos_pcie->elbi_base)) { ret = PTR_ERR(exynos_pcie->elbi_base); goto fail_bus_clk; } phy_base = platform_get_resource(pdev, IORESOURCE_MEM, 1); - exynos_pcie->phy_base = devm_ioremap_resource(&pdev->dev, phy_base); + exynos_pcie->phy_base = devm_ioremap_resource(dev, phy_base); if (IS_ERR(exynos_pcie->phy_base)) { ret = PTR_ERR(exynos_pcie->phy_base); goto fail_bus_clk; } block_base = platform_get_resource(pdev, IORESOURCE_MEM, 2); - exynos_pcie->block_base = devm_ioremap_resource(&pdev->dev, block_base); + exynos_pcie->block_base = devm_ioremap_resource(dev, block_base); if (IS_ERR(exynos_pcie->block_base)) { ret = PTR_ERR(exynos_pcie->block_base); goto fail_bus_clk; } - ret = exynos_add_pcie_port(pp, pdev); + ret = exynos_add_pcie_port(exynos_pcie, pdev); if (ret < 0) goto fail_bus_clk; diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index ead4a5c3480b..c8cefb078218 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -39,16 +39,15 @@ enum imx6_pcie_variants { }; struct imx6_pcie { + struct pcie_port pp; /* pp.dbi_base is DT 0th resource */ int reset_gpio; bool gpio_active_high; struct clk *pcie_bus; struct clk *pcie_phy; struct clk *pcie_inbound_axi; struct clk *pcie; - struct pcie_port pp; struct regmap *iomuxc_gpr; enum imx6_pcie_variants variant; - void __iomem *mem_base; u32 tx_deemph_gen1; u32 tx_deemph_gen2_3p5db; u32 tx_deemph_gen2_6db; @@ -96,14 +95,15 @@ struct imx6_pcie { #define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5) #define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3) -static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val) +static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val) { + struct pcie_port *pp = &imx6_pcie->pp; u32 val; u32 max_iterations = 10; u32 wait_counter = 0; do { - val = readl(dbi_base + PCIE_PHY_STAT); + val = dw_pcie_readl_rc(pp, PCIE_PHY_STAT); val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1; wait_counter++; @@ -116,123 +116,126 @@ static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val) return -ETIMEDOUT; } -static int pcie_phy_wait_ack(void __iomem *dbi_base, int addr) +static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr) { + struct pcie_port *pp = &imx6_pcie->pp; u32 val; int ret; val = addr << PCIE_PHY_CTRL_DATA_LOC; - writel(val, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val); val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC); - writel(val, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val); - ret = pcie_phy_poll_ack(dbi_base, 1); + ret = pcie_phy_poll_ack(imx6_pcie, 1); if (ret) return ret; val = addr << PCIE_PHY_CTRL_DATA_LOC; - writel(val, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val); - return pcie_phy_poll_ack(dbi_base, 0); + return pcie_phy_poll_ack(imx6_pcie, 0); } /* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */ -static int pcie_phy_read(void __iomem *dbi_base, int addr, int *data) +static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data) { + struct pcie_port *pp = &imx6_pcie->pp; u32 val, phy_ctl; int ret; - ret = pcie_phy_wait_ack(dbi_base, addr); + ret = pcie_phy_wait_ack(imx6_pcie, addr); if (ret) return ret; /* assert Read signal */ phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC; - writel(phy_ctl, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, phy_ctl); - ret = pcie_phy_poll_ack(dbi_base, 1); + ret = pcie_phy_poll_ack(imx6_pcie, 1); if (ret) return ret; - val = readl(dbi_base + PCIE_PHY_STAT); + val = dw_pcie_readl_rc(pp, PCIE_PHY_STAT); *data = val & 0xffff; /* deassert Read signal */ - writel(0x00, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, 0x00); - return pcie_phy_poll_ack(dbi_base, 0); + return pcie_phy_poll_ack(imx6_pcie, 0); } -static int pcie_phy_write(void __iomem *dbi_base, int addr, int data) +static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data) { + struct pcie_port *pp = &imx6_pcie->pp; u32 var; int ret; /* write addr */ /* cap addr */ - ret = pcie_phy_wait_ack(dbi_base, addr); + ret = pcie_phy_wait_ack(imx6_pcie, addr); if (ret) return ret; var = data << PCIE_PHY_CTRL_DATA_LOC; - writel(var, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var); /* capture data */ var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC); - writel(var, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var); - ret = pcie_phy_poll_ack(dbi_base, 1); + ret = pcie_phy_poll_ack(imx6_pcie, 1); if (ret) return ret; /* deassert cap data */ var = data << PCIE_PHY_CTRL_DATA_LOC; - writel(var, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var); /* wait for ack de-assertion */ - ret = pcie_phy_poll_ack(dbi_base, 0); + ret = pcie_phy_poll_ack(imx6_pcie, 0); if (ret) return ret; /* assert wr signal */ var = 0x1 << PCIE_PHY_CTRL_WR_LOC; - writel(var, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var); /* wait for ack */ - ret = pcie_phy_poll_ack(dbi_base, 1); + ret = pcie_phy_poll_ack(imx6_pcie, 1); if (ret) return ret; /* deassert wr signal */ var = data << PCIE_PHY_CTRL_DATA_LOC; - writel(var, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var); /* wait for ack de-assertion */ - ret = pcie_phy_poll_ack(dbi_base, 0); + ret = pcie_phy_poll_ack(imx6_pcie, 0); if (ret) return ret; - writel(0x0, dbi_base + PCIE_PHY_CTRL); + dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, 0x0); return 0; } -static void imx6_pcie_reset_phy(struct pcie_port *pp) +static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie) { u32 tmp; - pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &tmp); + pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp); tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN); - pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, tmp); + pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp); usleep_range(2000, 3000); - pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &tmp); + pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp); tmp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN); - pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, tmp); + pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp); } /* Added for PCI abort handling */ @@ -242,9 +245,9 @@ static int imx6q_pcie_abort_handler(unsigned long addr, return 0; } -static int imx6_pcie_assert_core_reset(struct pcie_port *pp) +static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie) { - struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); + struct pcie_port *pp = &imx6_pcie->pp; u32 val, gpr1, gpr12; switch (imx6_pcie->variant) { @@ -281,10 +284,10 @@ static int imx6_pcie_assert_core_reset(struct pcie_port *pp) if ((gpr1 & IMX6Q_GPR1_PCIE_REF_CLK_EN) && (gpr12 & IMX6Q_GPR12_PCIE_CTL_2)) { - val = readl(pp->dbi_base + PCIE_PL_PFLR); + val = dw_pcie_readl_rc(pp, PCIE_PL_PFLR); val &= ~PCIE_PL_PFLR_LINK_STATE_MASK; val |= PCIE_PL_PFLR_FORCE_LINK; - writel(val, pp->dbi_base + PCIE_PL_PFLR); + dw_pcie_writel_rc(pp, PCIE_PL_PFLR, val); regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6Q_GPR12_PCIE_CTL_2, 0 << 10); @@ -296,20 +299,19 @@ static int imx6_pcie_assert_core_reset(struct pcie_port *pp) IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16); break; } - - return 0; } static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) { struct pcie_port *pp = &imx6_pcie->pp; + struct device *dev = pp->dev; int ret = 0; switch (imx6_pcie->variant) { case IMX6SX: ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi); if (ret) { - dev_err(pp->dev, "unable to enable pcie_axi clock\n"); + dev_err(dev, "unable to enable pcie_axi clock\n"); break; } @@ -336,32 +338,33 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie) return ret; } -static int imx6_pcie_deassert_core_reset(struct pcie_port *pp) +static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie) { - struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); + struct pcie_port *pp = &imx6_pcie->pp; + struct device *dev = pp->dev; int ret; ret = clk_prepare_enable(imx6_pcie->pcie_phy); if (ret) { - dev_err(pp->dev, "unable to enable pcie_phy clock\n"); - goto err_pcie_phy; + dev_err(dev, "unable to enable pcie_phy clock\n"); + return; } ret = clk_prepare_enable(imx6_pcie->pcie_bus); if (ret) { - dev_err(pp->dev, "unable to enable pcie_bus clock\n"); + dev_err(dev, "unable to enable pcie_bus clock\n"); goto err_pcie_bus; } ret = clk_prepare_enable(imx6_pcie->pcie); if (ret) { - dev_err(pp->dev, "unable to enable pcie clock\n"); + dev_err(dev, "unable to enable pcie clock\n"); goto err_pcie; } ret = imx6_pcie_enable_ref_clk(imx6_pcie); if (ret) { - dev_err(pp->dev, "unable to enable pcie ref clock\n"); + dev_err(dev, "unable to enable pcie ref clock\n"); goto err_ref_clk; } @@ -392,7 +395,7 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp) break; } - return 0; + return; err_ref_clk: clk_disable_unprepare(imx6_pcie->pcie); @@ -400,14 +403,10 @@ err_pcie: clk_disable_unprepare(imx6_pcie->pcie_bus); err_pcie_bus: clk_disable_unprepare(imx6_pcie->pcie_phy); -err_pcie_phy: - return ret; } -static void imx6_pcie_init_phy(struct pcie_port *pp) +static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie) { - struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); - if (imx6_pcie->variant == IMX6SX) regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6SX_GPR12_PCIE_RX_EQ_MASK, @@ -439,45 +438,52 @@ static void imx6_pcie_init_phy(struct pcie_port *pp) imx6_pcie->tx_swing_low << 25); } -static int imx6_pcie_wait_for_link(struct pcie_port *pp) +static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie) { + struct pcie_port *pp = &imx6_pcie->pp; + struct device *dev = pp->dev; + /* check if the link is up or not */ if (!dw_pcie_wait_for_link(pp)) return 0; - dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", - readl(pp->dbi_base + PCIE_PHY_DEBUG_R0), - readl(pp->dbi_base + PCIE_PHY_DEBUG_R1)); + dev_dbg(dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", + dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0), + dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1)); return -ETIMEDOUT; } -static int imx6_pcie_wait_for_speed_change(struct pcie_port *pp) +static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie) { + struct pcie_port *pp = &imx6_pcie->pp; + struct device *dev = pp->dev; u32 tmp; unsigned int retries; for (retries = 0; retries < 200; retries++) { - tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); + tmp = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL); /* Test if the speed change finished. */ if (!(tmp & PORT_LOGIC_SPEED_CHANGE)) return 0; usleep_range(100, 1000); } - dev_err(pp->dev, "Speed change timeout\n"); + dev_err(dev, "Speed change timeout\n"); return -EINVAL; } static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg) { - struct pcie_port *pp = arg; + struct imx6_pcie *imx6_pcie = arg; + struct pcie_port *pp = &imx6_pcie->pp; return dw_handle_msi_irq(pp); } -static int imx6_pcie_establish_link(struct pcie_port *pp) +static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie) { - struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); + struct pcie_port *pp = &imx6_pcie->pp; + struct device *dev = pp->dev; u32 tmp; int ret; @@ -486,76 +492,73 @@ static int imx6_pcie_establish_link(struct pcie_port *pp) * started in Gen2 mode, there is a possibility the devices on the * bus will not be detected at all. This happens with PCIe switches. */ - tmp = readl(pp->dbi_base + PCIE_RC_LCR); + tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCR); tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1; - writel(tmp, pp->dbi_base + PCIE_RC_LCR); + dw_pcie_writel_rc(pp, PCIE_RC_LCR, tmp); /* Start LTSSM. */ regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, IMX6Q_GPR12_PCIE_CTL_2, 1 << 10); - ret = imx6_pcie_wait_for_link(pp); + ret = imx6_pcie_wait_for_link(imx6_pcie); if (ret) { - dev_info(pp->dev, "Link never came up\n"); + dev_info(dev, "Link never came up\n"); goto err_reset_phy; } if (imx6_pcie->link_gen == 2) { /* Allow Gen2 mode after the link is up. */ - tmp = readl(pp->dbi_base + PCIE_RC_LCR); + tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCR); tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2; - writel(tmp, pp->dbi_base + PCIE_RC_LCR); + dw_pcie_writel_rc(pp, PCIE_RC_LCR, tmp); } else { - dev_info(pp->dev, "Link: Gen2 disabled\n"); + dev_info(dev, "Link: Gen2 disabled\n"); } /* * Start Directed Speed Change so the best possible speed both link * partners support can be negotiated. */ - tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); + tmp = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL); tmp |= PORT_LOGIC_SPEED_CHANGE; - writel(tmp, pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); + dw_pcie_writel_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); - ret = imx6_pcie_wait_for_speed_change(pp); + ret = imx6_pcie_wait_for_speed_change(imx6_pcie); if (ret) { - dev_err(pp->dev, "Failed to bring link up!\n"); + dev_err(dev, "Failed to bring link up!\n"); goto err_reset_phy; } /* Make sure link training is finished as well! */ - ret = imx6_pcie_wait_for_link(pp); + ret = imx6_pcie_wait_for_link(imx6_pcie); if (ret) { - dev_err(pp->dev, "Failed to bring link up!\n"); + dev_err(dev, "Failed to bring link up!\n"); goto err_reset_phy; } - tmp = readl(pp->dbi_base + PCIE_RC_LCSR); - dev_info(pp->dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf); + tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCSR); + dev_info(dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf); return 0; err_reset_phy: - dev_dbg(pp->dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n", - readl(pp->dbi_base + PCIE_PHY_DEBUG_R0), - readl(pp->dbi_base + PCIE_PHY_DEBUG_R1)); - imx6_pcie_reset_phy(pp); - + dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n", + dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0), + dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1)); + imx6_pcie_reset_phy(imx6_pcie); return ret; } static void imx6_pcie_host_init(struct pcie_port *pp) { - imx6_pcie_assert_core_reset(pp); - - imx6_pcie_init_phy(pp); - - imx6_pcie_deassert_core_reset(pp); + struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp); + imx6_pcie_assert_core_reset(imx6_pcie); + imx6_pcie_init_phy(imx6_pcie); + imx6_pcie_deassert_core_reset(imx6_pcie); dw_pcie_setup_rc(pp); - - imx6_pcie_establish_link(pp); + imx6_pcie_establish_link(imx6_pcie); if (IS_ENABLED(CONFIG_PCI_MSI)) dw_pcie_msi_init(pp); @@ -563,7 +566,7 @@ static void imx6_pcie_host_init(struct pcie_port *pp) static int imx6_pcie_link_up(struct pcie_port *pp) { - return readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) & + return dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1) & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP; } @@ -572,24 +575,26 @@ static struct pcie_host_ops imx6_pcie_host_ops = { .host_init = imx6_pcie_host_init, }; -static int __init imx6_add_pcie_port(struct pcie_port *pp, - struct platform_device *pdev) +static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie, + struct platform_device *pdev) { + struct pcie_port *pp = &imx6_pcie->pp; + struct device *dev = pp->dev; int ret; if (IS_ENABLED(CONFIG_PCI_MSI)) { pp->msi_irq = platform_get_irq_byname(pdev, "msi"); if (pp->msi_irq <= 0) { - dev_err(&pdev->dev, "failed to get MSI irq\n"); + dev_err(dev, "failed to get MSI irq\n"); return -ENODEV; } - ret = devm_request_irq(&pdev->dev, pp->msi_irq, + ret = devm_request_irq(dev, pp->msi_irq, imx6_pcie_msi_handler, IRQF_SHARED | IRQF_NO_THREAD, - "mx6-pcie-msi", pp); + "mx6-pcie-msi", imx6_pcie); if (ret) { - dev_err(&pdev->dev, "failed to request MSI irq\n"); + dev_err(dev, "failed to request MSI irq\n"); return ret; } } @@ -599,7 +604,7 @@ static int __init imx6_add_pcie_port(struct pcie_port *pp, ret = dw_pcie_host_init(pp); if (ret) { - dev_err(&pdev->dev, "failed to initialize host\n"); + dev_err(dev, "failed to initialize host\n"); return ret; } @@ -608,75 +613,72 @@ static int __init imx6_add_pcie_port(struct pcie_port *pp, static int __init imx6_pcie_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct imx6_pcie *imx6_pcie; struct pcie_port *pp; - struct device_node *np = pdev->dev.of_node; struct resource *dbi_base; - struct device_node *node = pdev->dev.of_node; + struct device_node *node = dev->of_node; int ret; - imx6_pcie = devm_kzalloc(&pdev->dev, sizeof(*imx6_pcie), GFP_KERNEL); + imx6_pcie = devm_kzalloc(dev, sizeof(*imx6_pcie), GFP_KERNEL); if (!imx6_pcie) return -ENOMEM; pp = &imx6_pcie->pp; - pp->dev = &pdev->dev; + pp->dev = dev; imx6_pcie->variant = - (enum imx6_pcie_variants)of_device_get_match_data(&pdev->dev); + (enum imx6_pcie_variants)of_device_get_match_data(dev); /* Added for PCI abort handling */ hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0, "imprecise external abort"); dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base); + pp->dbi_base = devm_ioremap_resource(dev, dbi_base); if (IS_ERR(pp->dbi_base)) return PTR_ERR(pp->dbi_base); /* Fetch GPIOs */ - imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); - imx6_pcie->gpio_active_high = of_property_read_bool(np, + imx6_pcie->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0); + imx6_pcie->gpio_active_high = of_property_read_bool(node, "reset-gpio-active-high"); if (gpio_is_valid(imx6_pcie->reset_gpio)) { - ret = devm_gpio_request_one(&pdev->dev, imx6_pcie->reset_gpio, + ret = devm_gpio_request_one(dev, imx6_pcie->reset_gpio, imx6_pcie->gpio_active_high ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, "PCIe reset"); if (ret) { - dev_err(&pdev->dev, "unable to get reset gpio\n"); + dev_err(dev, "unable to get reset gpio\n"); return ret; } } /* Fetch clocks */ - imx6_pcie->pcie_phy = devm_clk_get(&pdev->dev, "pcie_phy"); + imx6_pcie->pcie_phy = devm_clk_get(dev, "pcie_phy"); if (IS_ERR(imx6_pcie->pcie_phy)) { - dev_err(&pdev->dev, - "pcie_phy clock source missing or invalid\n"); + dev_err(dev, "pcie_phy clock source missing or invalid\n"); return PTR_ERR(imx6_pcie->pcie_phy); } - imx6_pcie->pcie_bus = devm_clk_get(&pdev->dev, "pcie_bus"); + imx6_pcie->pcie_bus = devm_clk_get(dev, "pcie_bus"); if (IS_ERR(imx6_pcie->pcie_bus)) { - dev_err(&pdev->dev, - "pcie_bus clock source missing or invalid\n"); + dev_err(dev, "pcie_bus clock source missing or invalid\n"); return PTR_ERR(imx6_pcie->pcie_bus); } - imx6_pcie->pcie = devm_clk_get(&pdev->dev, "pcie"); + imx6_pcie->pcie = devm_clk_get(dev, "pcie"); if (IS_ERR(imx6_pcie->pcie)) { - dev_err(&pdev->dev, - "pcie clock source missing or invalid\n"); + dev_err(dev, "pcie clock source missing or invalid\n"); return PTR_ERR(imx6_pcie->pcie); } if (imx6_pcie->variant == IMX6SX) { - imx6_pcie->pcie_inbound_axi = devm_clk_get(&pdev->dev, + imx6_pcie->pcie_inbound_axi = devm_clk_get(dev, "pcie_inbound_axi"); if (IS_ERR(imx6_pcie->pcie_inbound_axi)) { - dev_err(&pdev->dev, + dev_err(dev, "pcie_incbound_axi clock missing or invalid\n"); return PTR_ERR(imx6_pcie->pcie_inbound_axi); } @@ -686,7 +688,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) imx6_pcie->iomuxc_gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); if (IS_ERR(imx6_pcie->iomuxc_gpr)) { - dev_err(&pdev->dev, "unable to find iomuxc registers\n"); + dev_err(dev, "unable to find iomuxc registers\n"); return PTR_ERR(imx6_pcie->iomuxc_gpr); } @@ -712,12 +714,12 @@ static int __init imx6_pcie_probe(struct platform_device *pdev) imx6_pcie->tx_swing_low = 127; /* Limit link speed */ - ret = of_property_read_u32(pp->dev->of_node, "fsl,max-link-speed", + ret = of_property_read_u32(node, "fsl,max-link-speed", &imx6_pcie->link_gen); if (ret) imx6_pcie->link_gen = 1; - ret = imx6_add_pcie_port(pp, pdev); + ret = imx6_add_pcie_port(imx6_pcie, pdev); if (ret < 0) return ret; @@ -730,7 +732,7 @@ static void imx6_pcie_shutdown(struct platform_device *pdev) struct imx6_pcie *imx6_pcie = platform_get_drvdata(pdev); /* bring down link, so bootloader gets clean state in case of reboot */ - imx6_pcie_assert_core_reset(&imx6_pcie->pp); + imx6_pcie_assert_core_reset(imx6_pcie); } static const struct of_device_id imx6_pcie_of_match[] = { diff --git a/drivers/pci/host/pci-keystone-dw.c b/drivers/pci/host/pci-keystone-dw.c index 41515092eb0d..9397c4667106 100644 --- a/drivers/pci/host/pci-keystone-dw.c +++ b/drivers/pci/host/pci-keystone-dw.c @@ -88,13 +88,24 @@ phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp) return ks_pcie->app.start + MSI_IRQ; } +static u32 ks_dw_app_readl(struct keystone_pcie *ks_pcie, u32 offset) +{ + return readl(ks_pcie->va_app_base + offset); +} + +static void ks_dw_app_writel(struct keystone_pcie *ks_pcie, u32 offset, u32 val) +{ + writel(val, ks_pcie->va_app_base + offset); +} + void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset) { struct pcie_port *pp = &ks_pcie->pp; + struct device *dev = pp->dev; u32 pending, vector; int src, virq; - pending = readl(ks_pcie->va_app_base + MSI0_IRQ_STATUS + (offset << 4)); + pending = ks_dw_app_readl(ks_pcie, MSI0_IRQ_STATUS + (offset << 4)); /* * MSI0 status bit 0-3 shows vectors 0, 8, 16, 24, MSI1 status bit @@ -104,7 +115,7 @@ void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset) if (BIT(src) & pending) { vector = offset + (src << 3); virq = irq_linear_revmap(pp->irq_domain, vector); - dev_dbg(pp->dev, "irq: bit %d, vector %d, virq %d\n", + dev_dbg(dev, "irq: bit %d, vector %d, virq %d\n", src, vector, virq); generic_handle_irq(virq); } @@ -124,9 +135,9 @@ static void ks_dw_pcie_msi_irq_ack(struct irq_data *d) offset = d->irq - irq_linear_revmap(pp->irq_domain, 0); update_reg_offset_bit_pos(offset, ®_offset, &bit_pos); - writel(BIT(bit_pos), - ks_pcie->va_app_base + MSI0_IRQ_STATUS + (reg_offset << 4)); - writel(reg_offset + MSI_IRQ_OFFSET, ks_pcie->va_app_base + IRQ_EOI); + ks_dw_app_writel(ks_pcie, MSI0_IRQ_STATUS + (reg_offset << 4), + BIT(bit_pos)); + ks_dw_app_writel(ks_pcie, IRQ_EOI, reg_offset + MSI_IRQ_OFFSET); } void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) @@ -135,8 +146,8 @@ void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); update_reg_offset_bit_pos(irq, ®_offset, &bit_pos); - writel(BIT(bit_pos), - ks_pcie->va_app_base + MSI0_IRQ_ENABLE_SET + (reg_offset << 4)); + ks_dw_app_writel(ks_pcie, MSI0_IRQ_ENABLE_SET + (reg_offset << 4), + BIT(bit_pos)); } void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) @@ -145,8 +156,8 @@ void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq) struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); update_reg_offset_bit_pos(irq, ®_offset, &bit_pos); - writel(BIT(bit_pos), - ks_pcie->va_app_base + MSI0_IRQ_ENABLE_CLR + (reg_offset << 4)); + ks_dw_app_writel(ks_pcie, MSI0_IRQ_ENABLE_CLR + (reg_offset << 4), + BIT(bit_pos)); } static void ks_dw_pcie_msi_irq_mask(struct irq_data *d) @@ -215,6 +226,7 @@ static const struct irq_domain_ops ks_dw_pcie_msi_domain_ops = { int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip) { struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); + struct device *dev = pp->dev; int i; pp->irq_domain = irq_domain_add_linear(ks_pcie->msi_intc_np, @@ -222,7 +234,7 @@ int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip) &ks_dw_pcie_msi_domain_ops, chip); if (!pp->irq_domain) { - dev_err(pp->dev, "irq domain init failed\n"); + dev_err(dev, "irq domain init failed\n"); return -ENXIO; } @@ -237,47 +249,47 @@ void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie) int i; for (i = 0; i < MAX_LEGACY_IRQS; i++) - writel(0x1, ks_pcie->va_app_base + IRQ_ENABLE_SET + (i << 4)); + ks_dw_app_writel(ks_pcie, IRQ_ENABLE_SET + (i << 4), 0x1); } void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset) { struct pcie_port *pp = &ks_pcie->pp; + struct device *dev = pp->dev; u32 pending; int virq; - pending = readl(ks_pcie->va_app_base + IRQ_STATUS + (offset << 4)); + pending = ks_dw_app_readl(ks_pcie, IRQ_STATUS + (offset << 4)); if (BIT(0) & pending) { virq = irq_linear_revmap(ks_pcie->legacy_irq_domain, offset); - dev_dbg(pp->dev, ": irq: irq_offset %d, virq %d\n", offset, - virq); + dev_dbg(dev, ": irq: irq_offset %d, virq %d\n", offset, virq); generic_handle_irq(virq); } /* EOI the INTx interrupt */ - writel(offset, ks_pcie->va_app_base + IRQ_EOI); + ks_dw_app_writel(ks_pcie, IRQ_EOI, offset); } -void ks_dw_pcie_enable_error_irq(void __iomem *reg_base) +void ks_dw_pcie_enable_error_irq(struct keystone_pcie *ks_pcie) { - writel(ERR_IRQ_ALL, reg_base + ERR_IRQ_ENABLE_SET); + ks_dw_app_writel(ks_pcie, ERR_IRQ_ENABLE_SET, ERR_IRQ_ALL); } -irqreturn_t ks_dw_pcie_handle_error_irq(struct device *dev, - void __iomem *reg_base) +irqreturn_t ks_dw_pcie_handle_error_irq(struct keystone_pcie *ks_pcie) { u32 status; - status = readl(reg_base + ERR_IRQ_STATUS_RAW) & ERR_IRQ_ALL; + status = ks_dw_app_readl(ks_pcie, ERR_IRQ_STATUS_RAW) & ERR_IRQ_ALL; if (!status) return IRQ_NONE; if (status & ERR_FATAL_IRQ) - dev_err(dev, "fatal error (status %#010x)\n", status); + dev_err(ks_pcie->pp.dev, "fatal error (status %#010x)\n", + status); /* Ack the IRQ; status bits are RW1C */ - writel(status, reg_base + ERR_IRQ_STATUS); + ks_dw_app_writel(ks_pcie, ERR_IRQ_STATUS, status); return IRQ_HANDLED; } @@ -322,15 +334,15 @@ static const struct irq_domain_ops ks_dw_pcie_legacy_irq_domain_ops = { * Since modification of dbi_cs2 involves different clock domain, read the * status back to ensure the transition is complete. */ -static void ks_dw_pcie_set_dbi_mode(void __iomem *reg_virt) +static void ks_dw_pcie_set_dbi_mode(struct keystone_pcie *ks_pcie) { u32 val; - writel(DBI_CS2_EN_VAL | readl(reg_virt + CMD_STATUS), - reg_virt + CMD_STATUS); + val = ks_dw_app_readl(ks_pcie, CMD_STATUS); + ks_dw_app_writel(ks_pcie, CMD_STATUS, DBI_CS2_EN_VAL | val); do { - val = readl(reg_virt + CMD_STATUS); + val = ks_dw_app_readl(ks_pcie, CMD_STATUS); } while (!(val & DBI_CS2_EN_VAL)); } @@ -340,15 +352,15 @@ static void ks_dw_pcie_set_dbi_mode(void __iomem *reg_virt) * Since modification of dbi_cs2 involves different clock domain, read the * status back to ensure the transition is complete. */ -static void ks_dw_pcie_clear_dbi_mode(void __iomem *reg_virt) +static void ks_dw_pcie_clear_dbi_mode(struct keystone_pcie *ks_pcie) { u32 val; - writel(~DBI_CS2_EN_VAL & readl(reg_virt + CMD_STATUS), - reg_virt + CMD_STATUS); + val = ks_dw_app_readl(ks_pcie, CMD_STATUS); + ks_dw_app_writel(ks_pcie, CMD_STATUS, ~DBI_CS2_EN_VAL & val); do { - val = readl(reg_virt + CMD_STATUS); + val = ks_dw_app_readl(ks_pcie, CMD_STATUS); } while (val & DBI_CS2_EN_VAL); } @@ -357,28 +369,29 @@ void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie) struct pcie_port *pp = &ks_pcie->pp; u32 start = pp->mem->start, end = pp->mem->end; int i, tr_size; + u32 val; /* Disable BARs for inbound access */ - ks_dw_pcie_set_dbi_mode(ks_pcie->va_app_base); - writel(0, pp->dbi_base + PCI_BASE_ADDRESS_0); - writel(0, pp->dbi_base + PCI_BASE_ADDRESS_1); - ks_dw_pcie_clear_dbi_mode(ks_pcie->va_app_base); + ks_dw_pcie_set_dbi_mode(ks_pcie); + dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 0); + dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_1, 0); + ks_dw_pcie_clear_dbi_mode(ks_pcie); /* Set outbound translation size per window division */ - writel(CFG_PCIM_WIN_SZ_IDX & 0x7, ks_pcie->va_app_base + OB_SIZE); + ks_dw_app_writel(ks_pcie, OB_SIZE, CFG_PCIM_WIN_SZ_IDX & 0x7); tr_size = (1 << (CFG_PCIM_WIN_SZ_IDX & 0x7)) * SZ_1M; /* Using Direct 1:1 mapping of RC <-> PCI memory space */ for (i = 0; (i < CFG_PCIM_WIN_CNT) && (start < end); i++) { - writel(start | 1, ks_pcie->va_app_base + OB_OFFSET_INDEX(i)); - writel(0, ks_pcie->va_app_base + OB_OFFSET_HI(i)); + ks_dw_app_writel(ks_pcie, OB_OFFSET_INDEX(i), start | 1); + ks_dw_app_writel(ks_pcie, OB_OFFSET_HI(i), 0); start += tr_size; } /* Enable OB translation */ - writel(OB_XLAT_EN_VAL | readl(ks_pcie->va_app_base + CMD_STATUS), - ks_pcie->va_app_base + CMD_STATUS); + val = ks_dw_app_readl(ks_pcie, CMD_STATUS); + ks_dw_app_writel(ks_pcie, CMD_STATUS, OB_XLAT_EN_VAL | val); } /** @@ -418,7 +431,7 @@ static void __iomem *ks_pcie_cfg_setup(struct keystone_pcie *ks_pcie, u8 bus, if (bus != 1) regval |= BIT(24); - writel(regval, ks_pcie->va_app_base + CFG_SETUP); + ks_dw_app_writel(ks_pcie, CFG_SETUP, regval); return pp->va_cfg0_base; } @@ -456,19 +469,19 @@ void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp) struct keystone_pcie *ks_pcie = to_keystone_pcie(pp); /* Configure and set up BAR0 */ - ks_dw_pcie_set_dbi_mode(ks_pcie->va_app_base); + ks_dw_pcie_set_dbi_mode(ks_pcie); /* Enable BAR0 */ - writel(1, pp->dbi_base + PCI_BASE_ADDRESS_0); - writel(SZ_4K - 1, pp->dbi_base + PCI_BASE_ADDRESS_0); + dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 1); + dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, SZ_4K - 1); - ks_dw_pcie_clear_dbi_mode(ks_pcie->va_app_base); + ks_dw_pcie_clear_dbi_mode(ks_pcie); /* * For BAR0, just setting bus address for inbound writes (MSI) should * be sufficient. Use physical address to avoid any conflicts. */ - writel(ks_pcie->app.start, pp->dbi_base + PCI_BASE_ADDRESS_0); + dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, ks_pcie->app.start); } /** @@ -476,8 +489,9 @@ void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp) */ int ks_dw_pcie_link_up(struct pcie_port *pp) { - u32 val = readl(pp->dbi_base + DEBUG0); + u32 val; + val = dw_pcie_readl_rc(pp, DEBUG0); return (val & LTSSM_STATE_MASK) == LTSSM_STATE_L0; } @@ -486,13 +500,13 @@ void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie) u32 val; /* Disable Link training */ - val = readl(ks_pcie->va_app_base + CMD_STATUS); + val = ks_dw_app_readl(ks_pcie, CMD_STATUS); val &= ~LTSSM_EN_VAL; - writel(LTSSM_EN_VAL | val, ks_pcie->va_app_base + CMD_STATUS); + ks_dw_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val); /* Initiate Link Training */ - val = readl(ks_pcie->va_app_base + CMD_STATUS); - writel(LTSSM_EN_VAL | val, ks_pcie->va_app_base + CMD_STATUS); + val = ks_dw_app_readl(ks_pcie, CMD_STATUS); + ks_dw_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val); } /** @@ -506,12 +520,13 @@ int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie, struct device_node *msi_intc_np) { struct pcie_port *pp = &ks_pcie->pp; - struct platform_device *pdev = to_platform_device(pp->dev); + struct device *dev = pp->dev; + struct platform_device *pdev = to_platform_device(dev); struct resource *res; /* Index 0 is the config reg. space address */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pp->dbi_base = devm_ioremap_resource(pp->dev, res); + pp->dbi_base = devm_ioremap_resource(dev, res); if (IS_ERR(pp->dbi_base)) return PTR_ERR(pp->dbi_base); @@ -524,7 +539,7 @@ int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie, /* Index 1 is the application reg. space address */ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - ks_pcie->va_app_base = devm_ioremap_resource(pp->dev, res); + ks_pcie->va_app_base = devm_ioremap_resource(dev, res); if (IS_ERR(ks_pcie->va_app_base)) return PTR_ERR(ks_pcie->va_app_base); @@ -537,7 +552,7 @@ int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie, &ks_dw_pcie_legacy_irq_domain_ops, NULL); if (!ks_pcie->legacy_irq_domain) { - dev_err(pp->dev, "Failed to add irq domain for legacy irqs\n"); + dev_err(dev, "Failed to add irq domain for legacy irqs\n"); return -EINVAL; } diff --git a/drivers/pci/host/pci-keystone.c b/drivers/pci/host/pci-keystone.c index 82b461b5b08a..043c19a05da1 100644 --- a/drivers/pci/host/pci-keystone.c +++ b/drivers/pci/host/pci-keystone.c @@ -89,12 +89,13 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, quirk_limit_mrrs); static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie) { struct pcie_port *pp = &ks_pcie->pp; + struct device *dev = pp->dev; unsigned int retries; dw_pcie_setup_rc(pp); if (dw_pcie_link_up(pp)) { - dev_err(pp->dev, "Link already up\n"); + dev_err(dev, "Link already up\n"); return 0; } @@ -105,7 +106,7 @@ static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie) return 0; } - dev_err(pp->dev, "phy link never came up\n"); + dev_err(dev, "phy link never came up\n"); return -ETIMEDOUT; } @@ -115,9 +116,10 @@ static void ks_pcie_msi_irq_handler(struct irq_desc *desc) struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); u32 offset = irq - ks_pcie->msi_host_irqs[0]; struct pcie_port *pp = &ks_pcie->pp; + struct device *dev = pp->dev; struct irq_chip *chip = irq_desc_get_chip(desc); - dev_dbg(pp->dev, "%s, irq %d\n", __func__, irq); + dev_dbg(dev, "%s, irq %d\n", __func__, irq); /* * The chained irq handler installation would have replaced normal @@ -142,10 +144,11 @@ static void ks_pcie_legacy_irq_handler(struct irq_desc *desc) unsigned int irq = irq_desc_get_irq(desc); struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc); struct pcie_port *pp = &ks_pcie->pp; + struct device *dev = pp->dev; u32 irq_offset = irq - ks_pcie->legacy_host_irqs[0]; struct irq_chip *chip = irq_desc_get_chip(desc); - dev_dbg(pp->dev, ": Handling legacy irq %d\n", irq); + dev_dbg(dev, ": Handling legacy irq %d\n", irq); /* * The chained irq handler installation would have replaced normal @@ -234,7 +237,7 @@ static void ks_pcie_setup_interrupts(struct keystone_pcie *ks_pcie) } if (ks_pcie->error_irq > 0) - ks_dw_pcie_enable_error_irq(ks_pcie->va_app_base); + ks_dw_pcie_enable_error_irq(ks_pcie); } /* @@ -302,14 +305,14 @@ static irqreturn_t pcie_err_irq_handler(int irq, void *priv) { struct keystone_pcie *ks_pcie = priv; - return ks_dw_pcie_handle_error_irq(ks_pcie->pp.dev, - ks_pcie->va_app_base); + return ks_dw_pcie_handle_error_irq(ks_pcie); } static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie, struct platform_device *pdev) { struct pcie_port *pp = &ks_pcie->pp; + struct device *dev = pp->dev; int ret; ret = ks_pcie_get_irq_controller_info(ks_pcie, @@ -332,12 +335,12 @@ static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie, */ ks_pcie->error_irq = irq_of_parse_and_map(ks_pcie->np, 0); if (ks_pcie->error_irq <= 0) - dev_info(&pdev->dev, "no error IRQ defined\n"); + dev_info(dev, "no error IRQ defined\n"); else { ret = request_irq(ks_pcie->error_irq, pcie_err_irq_handler, IRQF_SHARED, "pcie-error-irq", ks_pcie); if (ret < 0) { - dev_err(&pdev->dev, "failed to request error IRQ %d\n", + dev_err(dev, "failed to request error IRQ %d\n", ks_pcie->error_irq); return ret; } @@ -347,7 +350,7 @@ static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie, pp->ops = &keystone_pcie_host_ops; ret = ks_dw_pcie_host_init(ks_pcie, ks_pcie->msi_intc_np); if (ret) { - dev_err(&pdev->dev, "failed to initialize host\n"); + dev_err(dev, "failed to initialize host\n"); return ret; } @@ -381,12 +384,12 @@ static int __init ks_pcie_probe(struct platform_device *pdev) struct phy *phy; int ret; - ks_pcie = devm_kzalloc(&pdev->dev, sizeof(*ks_pcie), - GFP_KERNEL); + ks_pcie = devm_kzalloc(dev, sizeof(*ks_pcie), GFP_KERNEL); if (!ks_pcie) return -ENOMEM; pp = &ks_pcie->pp; + pp->dev = dev; /* initialize SerDes Phy if present */ phy = devm_phy_get(dev, "pcie-phy"); @@ -408,7 +411,6 @@ static int __init ks_pcie_probe(struct platform_device *pdev) devm_iounmap(dev, reg_p); devm_release_mem_region(dev, res->start, resource_size(res)); - pp->dev = dev; ks_pcie->np = dev->of_node; platform_set_drvdata(pdev, ks_pcie); ks_pcie->clk = devm_clk_get(dev, "pcie"); diff --git a/drivers/pci/host/pci-keystone.h b/drivers/pci/host/pci-keystone.h index a5b0cb2ba4d7..bc54bafda068 100644 --- a/drivers/pci/host/pci-keystone.h +++ b/drivers/pci/host/pci-keystone.h @@ -17,8 +17,8 @@ #define MAX_LEGACY_HOST_IRQS 4 struct keystone_pcie { + struct pcie_port pp; /* pp.dbi_base is DT 0th res */ struct clk *clk; - struct pcie_port pp; /* PCI Device ID */ u32 device_id; int num_legacy_host_irqs; @@ -34,7 +34,7 @@ struct keystone_pcie { int error_irq; /* Application register space */ - void __iomem *va_app_base; + void __iomem *va_app_base; /* DT 1st resource */ struct resource app; }; @@ -45,9 +45,8 @@ phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp); /* Keystone specific PCI controller APIs */ void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie); void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset); -void ks_dw_pcie_enable_error_irq(void __iomem *reg_base); -irqreturn_t ks_dw_pcie_handle_error_irq(struct device *dev, - void __iomem *reg_base); +void ks_dw_pcie_enable_error_irq(struct keystone_pcie *ks_pcie); +irqreturn_t ks_dw_pcie_handle_error_irq(struct keystone_pcie *ks_pcie); int ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie, struct device_node *msi_intc_np); int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c index 114ba819277a..2cb7315e26d0 100644 --- a/drivers/pci/host/pci-layerscape.c +++ b/drivers/pci/host/pci-layerscape.c @@ -45,10 +45,9 @@ struct ls_pcie_drvdata { }; struct ls_pcie { - void __iomem *dbi; + struct pcie_port pp; /* pp.dbi_base is DT regs */ void __iomem *lut; struct regmap *scfg; - struct pcie_port pp; const struct ls_pcie_drvdata *drvdata; int index; }; @@ -59,7 +58,7 @@ static bool ls_pcie_is_bridge(struct ls_pcie *pcie) { u32 header_type; - header_type = ioread8(pcie->dbi + PCI_HEADER_TYPE); + header_type = ioread8(pcie->pp.dbi_base + PCI_HEADER_TYPE); header_type &= 0x7f; return header_type == PCI_HEADER_TYPE_BRIDGE; @@ -68,13 +67,13 @@ static bool ls_pcie_is_bridge(struct ls_pcie *pcie) /* Clear multi-function bit */ static void ls_pcie_clear_multifunction(struct ls_pcie *pcie) { - iowrite8(PCI_HEADER_TYPE_BRIDGE, pcie->dbi + PCI_HEADER_TYPE); + iowrite8(PCI_HEADER_TYPE_BRIDGE, pcie->pp.dbi_base + PCI_HEADER_TYPE); } /* Fix class value */ static void ls_pcie_fix_class(struct ls_pcie *pcie) { - iowrite16(PCI_CLASS_BRIDGE_PCI, pcie->dbi + PCI_CLASS_DEVICE); + iowrite16(PCI_CLASS_BRIDGE_PCI, pcie->pp.dbi_base + PCI_CLASS_DEVICE); } /* Drop MSG TLP except for Vendor MSG */ @@ -82,9 +81,9 @@ static void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie) { u32 val; - val = ioread32(pcie->dbi + PCIE_STRFMR1); + val = ioread32(pcie->pp.dbi_base + PCIE_STRFMR1); val &= 0xDFFFFFFF; - iowrite32(val, pcie->dbi + PCIE_STRFMR1); + iowrite32(val, pcie->pp.dbi_base + PCIE_STRFMR1); } static int ls1021_pcie_link_up(struct pcie_port *pp) @@ -106,18 +105,19 @@ static int ls1021_pcie_link_up(struct pcie_port *pp) static void ls1021_pcie_host_init(struct pcie_port *pp) { + struct device *dev = pp->dev; struct ls_pcie *pcie = to_ls_pcie(pp); u32 index[2]; - pcie->scfg = syscon_regmap_lookup_by_phandle(pp->dev->of_node, + pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,pcie-scfg"); if (IS_ERR(pcie->scfg)) { - dev_err(pp->dev, "No syscfg phandle specified\n"); + dev_err(dev, "No syscfg phandle specified\n"); pcie->scfg = NULL; return; } - if (of_property_read_u32_array(pp->dev->of_node, + if (of_property_read_u32_array(dev->of_node, "fsl,pcie-scfg", index, 2)) { pcie->scfg = NULL; return; @@ -148,18 +148,19 @@ static void ls_pcie_host_init(struct pcie_port *pp) { struct ls_pcie *pcie = to_ls_pcie(pp); - iowrite32(1, pcie->dbi + PCIE_DBI_RO_WR_EN); + iowrite32(1, pcie->pp.dbi_base + PCIE_DBI_RO_WR_EN); ls_pcie_fix_class(pcie); ls_pcie_clear_multifunction(pcie); ls_pcie_drop_msg_tlp(pcie); - iowrite32(0, pcie->dbi + PCIE_DBI_RO_WR_EN); + iowrite32(0, pcie->pp.dbi_base + PCIE_DBI_RO_WR_EN); } static int ls_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip) { + struct device *dev = pp->dev; + struct device_node *np = dev->of_node; struct device_node *msi_node; - struct device_node *np = pp->dev->of_node; /* * The MSI domain is set by the generic of_msi_configure(). This @@ -169,7 +170,7 @@ static int ls_pcie_msi_host_init(struct pcie_port *pp, */ msi_node = of_parse_phandle(np, "msi-parent", 0); if (!msi_node) { - dev_err(pp->dev, "failed to find msi-parent\n"); + dev_err(dev, "failed to find msi-parent\n"); return -EINVAL; } @@ -212,19 +213,15 @@ static const struct of_device_id ls_pcie_of_match[] = { { }, }; -static int __init ls_add_pcie_port(struct pcie_port *pp, - struct platform_device *pdev) +static int __init ls_add_pcie_port(struct ls_pcie *pcie) { + struct pcie_port *pp = &pcie->pp; + struct device *dev = pp->dev; int ret; - struct ls_pcie *pcie = to_ls_pcie(pp); - - pp->dev = &pdev->dev; - pp->dbi_base = pcie->dbi; - pp->ops = pcie->drvdata->ops; ret = dw_pcie_host_init(pp); if (ret) { - dev_err(pp->dev, "failed to initialize host\n"); + dev_err(dev, "failed to initialize host\n"); return ret; } @@ -233,38 +230,42 @@ static int __init ls_add_pcie_port(struct pcie_port *pp, static int __init ls_pcie_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; const struct of_device_id *match; struct ls_pcie *pcie; + struct pcie_port *pp; struct resource *dbi_base; int ret; - match = of_match_device(ls_pcie_of_match, &pdev->dev); + match = of_match_device(ls_pcie_of_match, dev); if (!match) return -ENODEV; - pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) return -ENOMEM; + pp = &pcie->pp; + pp->dev = dev; + pp->ops = pcie->drvdata->ops; + dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); - pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base); - if (IS_ERR(pcie->dbi)) { - dev_err(&pdev->dev, "missing *regs* space\n"); - return PTR_ERR(pcie->dbi); + pcie->pp.dbi_base = devm_ioremap_resource(dev, dbi_base); + if (IS_ERR(pcie->pp.dbi_base)) { + dev_err(dev, "missing *regs* space\n"); + return PTR_ERR(pcie->pp.dbi_base); } pcie->drvdata = match->data; - pcie->lut = pcie->dbi + pcie->drvdata->lut_offset; + pcie->lut = pcie->pp.dbi_base + pcie->drvdata->lut_offset; if (!ls_pcie_is_bridge(pcie)) return -ENODEV; - ret = ls_add_pcie_port(&pcie->pp, pdev); + ret = ls_add_pcie_port(pcie); if (ret < 0) return ret; - platform_set_drvdata(pdev, pcie); - return 0; } diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 307f81d6b479..45a89d969700 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -1190,13 +1190,13 @@ static void mvebu_pcie_powerdown(struct mvebu_pcie_port *port) static int mvebu_pcie_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct mvebu_pcie *pcie; - struct device_node *np = pdev->dev.of_node; + struct device_node *np = dev->of_node; struct device_node *child; int num, i, ret; - pcie = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pcie), - GFP_KERNEL); + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) return -ENOMEM; @@ -1206,7 +1206,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) /* Get the PCIe memory and I/O aperture */ mvebu_mbus_get_pcie_mem_aperture(&pcie->mem); if (resource_size(&pcie->mem) == 0) { - dev_err(&pdev->dev, "invalid memory aperture size\n"); + dev_err(dev, "invalid memory aperture size\n"); return -EINVAL; } @@ -1224,20 +1224,18 @@ static int mvebu_pcie_probe(struct platform_device *pdev) /* Get the bus range */ ret = of_pci_parse_bus_range(np, &pcie->busn); if (ret) { - dev_err(&pdev->dev, "failed to parse bus-range property: %d\n", - ret); + dev_err(dev, "failed to parse bus-range property: %d\n", ret); return ret; } - num = of_get_available_child_count(pdev->dev.of_node); + num = of_get_available_child_count(np); - pcie->ports = devm_kcalloc(&pdev->dev, num, sizeof(*pcie->ports), - GFP_KERNEL); + pcie->ports = devm_kcalloc(dev, num, sizeof(*pcie->ports), GFP_KERNEL); if (!pcie->ports) return -ENOMEM; i = 0; - for_each_available_child_of_node(pdev->dev.of_node, child) { + for_each_available_child_of_node(np, child) { struct mvebu_pcie_port *port = &pcie->ports[i]; ret = mvebu_pcie_parse_port(pcie, port, child); @@ -1266,8 +1264,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) port->base = mvebu_pcie_map_registers(pdev, child, port); if (IS_ERR(port->base)) { - dev_err(&pdev->dev, "%s: cannot map registers\n", - port->name); + dev_err(dev, "%s: cannot map registers\n", port->name); port->base = NULL; mvebu_pcie_powerdown(port); continue; diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index 597566f96f5e..1eeefa4df64c 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -154,10 +154,11 @@ static int rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) static irqreturn_t rcar_pci_err_irq(int irq, void *pw) { struct rcar_pci_priv *priv = pw; + struct device *dev = priv->dev; u32 status = ioread32(priv->reg + RCAR_PCI_INT_STATUS_REG); if (status & RCAR_PCI_INT_ALLERRORS) { - dev_err(priv->dev, "error irq: status %08x\n", status); + dev_err(dev, "error irq: status %08x\n", status); /* clear the error(s) */ iowrite32(status & RCAR_PCI_INT_ALLERRORS, @@ -170,13 +171,14 @@ static irqreturn_t rcar_pci_err_irq(int irq, void *pw) static void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) { + struct device *dev = priv->dev; int ret; u32 val; - ret = devm_request_irq(priv->dev, priv->irq, rcar_pci_err_irq, + ret = devm_request_irq(dev, priv->irq, rcar_pci_err_irq, IRQF_SHARED, "error irq", priv); if (ret) { - dev_err(priv->dev, "cannot claim IRQ for error handling\n"); + dev_err(dev, "cannot claim IRQ for error handling\n"); return; } @@ -192,15 +194,16 @@ static inline void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) { } static int rcar_pci_setup(int nr, struct pci_sys_data *sys) { struct rcar_pci_priv *priv = sys->private_data; + struct device *dev = priv->dev; void __iomem *reg = priv->reg; u32 val; int ret; - pm_runtime_enable(priv->dev); - pm_runtime_get_sync(priv->dev); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); val = ioread32(reg + RCAR_PCI_UNIT_REV_REG); - dev_info(priv->dev, "PCI: bus%u revision %x\n", sys->busnr, val); + dev_info(dev, "PCI: bus%u revision %x\n", sys->busnr, val); /* Disable Direct Power Down State and assert reset */ val = ioread32(reg + RCAR_USBCTR_REG) & ~RCAR_USBCTR_DIRPD; @@ -275,7 +278,7 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys) /* Add PCI resources */ pci_add_resource(&sys->resources, &priv->mem_res); - ret = devm_request_pci_bus_resources(priv->dev, &sys->resources); + ret = devm_request_pci_bus_resources(dev, &sys->resources); if (ret < 0) return ret; @@ -311,6 +314,7 @@ static int pci_dma_range_parser_init(struct of_pci_range_parser *parser, static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci, struct device_node *np) { + struct device *dev = pci->dev; struct of_pci_range range; struct of_pci_range_parser parser; int index = 0; @@ -331,14 +335,14 @@ static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci, /* Catch HW limitations */ if (!(range.flags & IORESOURCE_PREFETCH)) { - dev_err(pci->dev, "window must be prefetchable\n"); + dev_err(dev, "window must be prefetchable\n"); return -EINVAL; } if (pci->window_addr) { u32 lowaddr = 1 << (ffs(pci->window_addr) - 1); if (lowaddr < pci->window_size) { - dev_err(pci->dev, "invalid window size/addr\n"); + dev_err(dev, "invalid window size/addr\n"); return -EINVAL; } } @@ -350,6 +354,7 @@ static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci, static int rcar_pci_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct resource *cfg_res, *mem_res; struct rcar_pci_priv *priv; void __iomem *reg; @@ -357,7 +362,7 @@ static int rcar_pci_probe(struct platform_device *pdev) void *hw_private[1]; cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - reg = devm_ioremap_resource(&pdev->dev, cfg_res); + reg = devm_ioremap_resource(dev, cfg_res); if (IS_ERR(reg)) return PTR_ERR(reg); @@ -368,8 +373,7 @@ static int rcar_pci_probe(struct platform_device *pdev) if (mem_res->start & 0xFFFF) return -EINVAL; - priv = devm_kzalloc(&pdev->dev, - sizeof(struct rcar_pci_priv), GFP_KERNEL); + priv = devm_kzalloc(dev, sizeof(struct rcar_pci_priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -378,10 +382,10 @@ static int rcar_pci_probe(struct platform_device *pdev) priv->irq = platform_get_irq(pdev, 0); priv->reg = reg; - priv->dev = &pdev->dev; + priv->dev = dev; if (priv->irq < 0) { - dev_err(&pdev->dev, "no valid irq found\n"); + dev_err(dev, "no valid irq found\n"); return priv->irq; } @@ -390,23 +394,23 @@ static int rcar_pci_probe(struct platform_device *pdev) priv->window_pci = 0x40000000; priv->window_size = SZ_1G; - if (pdev->dev.of_node) { + if (dev->of_node) { struct resource busnr; int ret; - ret = of_pci_parse_bus_range(pdev->dev.of_node, &busnr); + ret = of_pci_parse_bus_range(dev->of_node, &busnr); if (ret < 0) { - dev_err(&pdev->dev, "failed to parse bus-range\n"); + dev_err(dev, "failed to parse bus-range\n"); return ret; } priv->busnr = busnr.start; if (busnr.end != busnr.start) - dev_warn(&pdev->dev, "only one bus number supported\n"); + dev_warn(dev, "only one bus number supported\n"); - ret = rcar_pci_parse_map_dma_ranges(priv, pdev->dev.of_node); + ret = rcar_pci_parse_map_dma_ranges(priv, dev->of_node); if (ret < 0) { - dev_err(&pdev->dev, "failed to parse dma-range\n"); + dev_err(dev, "failed to parse dma-range\n"); return ret; } } else { @@ -421,7 +425,7 @@ static int rcar_pci_probe(struct platform_device *pdev) hw.map_irq = rcar_pci_map_irq; hw.ops = &rcar_pci_ops; hw.setup = rcar_pci_setup; - pci_common_init_dev(&pdev->dev, &hw); + pci_common_init_dev(dev, &hw); return 0; } diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index e2a8e4cab22e..8dfccf733241 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -384,6 +384,7 @@ static unsigned long tegra_pcie_conf_offset(unsigned int devfn, int where) static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie, unsigned int busnr) { + struct device *dev = pcie->dev; pgprot_t prot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_XN | L_PTE_MT_DEV_SHARED | L_PTE_SHARED); phys_addr_t cs = pcie->cs->start; @@ -413,8 +414,7 @@ static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie, err = ioremap_page_range(virt, virt + SZ_64K, phys, prot); if (err < 0) { - dev_err(pcie->dev, "ioremap_page_range() failed: %d\n", - err); + dev_err(dev, "ioremap_page_range() failed: %d\n", err); goto unmap; } } @@ -462,6 +462,7 @@ static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus, int where) { struct tegra_pcie *pcie = sys_to_pcie(bus->sysdata); + struct device *dev = pcie->dev; void __iomem *addr = NULL; if (bus->number == 0) { @@ -482,8 +483,7 @@ static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus, addr = (void __iomem *)b->area->addr; if (!addr) { - dev_err(pcie->dev, - "failed to map cfg. space for bus %u\n", + dev_err(dev, "failed to map cfg. space for bus %u\n", bus->number); return NULL; } @@ -584,12 +584,13 @@ static void tegra_pcie_port_disable(struct tegra_pcie_port *port) static void tegra_pcie_port_free(struct tegra_pcie_port *port) { struct tegra_pcie *pcie = port->pcie; + struct device *dev = pcie->dev; - devm_iounmap(pcie->dev, port->base); - devm_release_mem_region(pcie->dev, port->regs.start, + devm_iounmap(dev, port->base); + devm_release_mem_region(dev, port->regs.start, resource_size(&port->regs)); list_del(&port->list); - devm_kfree(pcie->dev, port); + devm_kfree(dev, port); } /* Tegra PCIE root complex wrongly reports device class */ @@ -612,12 +613,13 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable); static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) { struct tegra_pcie *pcie = sys_to_pcie(sys); + struct device *dev = pcie->dev; int err; sys->mem_offset = pcie->offset.mem; sys->io_offset = pcie->offset.io; - err = devm_request_resource(pcie->dev, &iomem_resource, &pcie->io); + err = devm_request_resource(dev, &iomem_resource, &pcie->io); if (err < 0) return err; @@ -631,7 +633,7 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) sys->mem_offset); pci_add_resource(&sys->resources, &pcie->busn); - err = devm_request_pci_bus_resources(pcie->dev, &sys->resources); + err = devm_request_pci_bus_resources(dev, &sys->resources); if (err < 0) return err; @@ -672,6 +674,7 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg) "Peer2Peer error", }; struct tegra_pcie *pcie = arg; + struct device *dev = pcie->dev; u32 code, signature; code = afi_readl(pcie, AFI_INTR_CODE) & AFI_INTR_CODE_MASK; @@ -689,11 +692,9 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg) * happen a lot during enumeration */ if (code == AFI_INTR_MASTER_ABORT) - dev_dbg(pcie->dev, "%s, signature: %08x\n", err_msg[code], - signature); + dev_dbg(dev, "%s, signature: %08x\n", err_msg[code], signature); else - dev_err(pcie->dev, "%s, signature: %08x\n", err_msg[code], - signature); + dev_err(dev, "%s, signature: %08x\n", err_msg[code], signature); if (code == AFI_INTR_TARGET_ABORT || code == AFI_INTR_MASTER_ABORT || code == AFI_INTR_FPCI_DECODE_ERROR) { @@ -701,9 +702,9 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg) u64 address = (u64)fpci << 32 | (signature & 0xfffffffc); if (code == AFI_INTR_MASTER_ABORT) - dev_dbg(pcie->dev, " FPCI address: %10llx\n", address); + dev_dbg(dev, " FPCI address: %10llx\n", address); else - dev_err(pcie->dev, " FPCI address: %10llx\n", address); + dev_err(dev, " FPCI address: %10llx\n", address); } return IRQ_HANDLED; @@ -793,6 +794,7 @@ static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout) static int tegra_pcie_phy_enable(struct tegra_pcie *pcie) { + struct device *dev = pcie->dev; const struct tegra_pcie_soc *soc = pcie->soc; u32 value; int err; @@ -829,7 +831,7 @@ static int tegra_pcie_phy_enable(struct tegra_pcie *pcie) /* wait for the PLL to lock */ err = tegra_pcie_pll_wait(pcie, 500); if (err < 0) { - dev_err(pcie->dev, "PLL failed to lock: %d\n", err); + dev_err(dev, "PLL failed to lock: %d\n", err); return err; } @@ -859,7 +861,7 @@ static int tegra_pcie_phy_disable(struct tegra_pcie *pcie) /* override IDDQ */ value = pads_readl(pcie, PADS_CTL); value |= PADS_CTL_IDDQ_1L; - pads_writel(pcie, PADS_CTL, value); + pads_writel(pcie, value, PADS_CTL); /* reset PLL */ value = pads_readl(pcie, soc->pads_pll_ctl); @@ -880,8 +882,7 @@ static int tegra_pcie_port_phy_power_on(struct tegra_pcie_port *port) for (i = 0; i < port->lanes; i++) { err = phy_power_on(port->phys[i]); if (err < 0) { - dev_err(dev, "failed to power on PHY#%u: %d\n", i, - err); + dev_err(dev, "failed to power on PHY#%u: %d\n", i, err); return err; } } @@ -909,6 +910,7 @@ static int tegra_pcie_port_phy_power_off(struct tegra_pcie_port *port) static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie) { + struct device *dev = pcie->dev; const struct tegra_pcie_soc *soc = pcie->soc; struct tegra_pcie_port *port; int err; @@ -920,7 +922,7 @@ static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie) err = tegra_pcie_phy_enable(pcie); if (err < 0) - dev_err(pcie->dev, "failed to power on PHY: %d\n", err); + dev_err(dev, "failed to power on PHY: %d\n", err); return err; } @@ -928,7 +930,7 @@ static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie) list_for_each_entry(port, &pcie->ports, list) { err = tegra_pcie_port_phy_power_on(port); if (err < 0) { - dev_err(pcie->dev, + dev_err(dev, "failed to power on PCIe port %u PHY: %d\n", port->index, err); return err; @@ -946,6 +948,7 @@ static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie) static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie) { + struct device *dev = pcie->dev; struct tegra_pcie_port *port; int err; @@ -956,8 +959,7 @@ static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie) err = tegra_pcie_phy_disable(pcie); if (err < 0) - dev_err(pcie->dev, "failed to power off PHY: %d\n", - err); + dev_err(dev, "failed to power off PHY: %d\n", err); return err; } @@ -965,7 +967,7 @@ static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie) list_for_each_entry(port, &pcie->ports, list) { err = tegra_pcie_port_phy_power_off(port); if (err < 0) { - dev_err(pcie->dev, + dev_err(dev, "failed to power off PCIe port %u PHY: %d\n", port->index, err); return err; @@ -977,6 +979,7 @@ static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie) static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) { + struct device *dev = pcie->dev; const struct tegra_pcie_soc *soc = pcie->soc; struct tegra_pcie_port *port; unsigned long value; @@ -1016,7 +1019,7 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) err = tegra_pcie_phy_power_on(pcie); if (err < 0) { - dev_err(pcie->dev, "failed to power on PHY(s): %d\n", err); + dev_err(dev, "failed to power on PHY(s): %d\n", err); return err; } @@ -1049,13 +1052,14 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) static void tegra_pcie_power_off(struct tegra_pcie *pcie) { + struct device *dev = pcie->dev; int err; /* TODO: disable and unprepare clocks? */ err = tegra_pcie_phy_power_off(pcie); if (err < 0) - dev_err(pcie->dev, "failed to power off PHY(s): %d\n", err); + dev_err(dev, "failed to power off PHY(s): %d\n", err); reset_control_assert(pcie->pcie_xrst); reset_control_assert(pcie->afi_rst); @@ -1065,11 +1069,12 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie) err = regulator_bulk_disable(pcie->num_supplies, pcie->supplies); if (err < 0) - dev_warn(pcie->dev, "failed to disable regulators: %d\n", err); + dev_warn(dev, "failed to disable regulators: %d\n", err); } static int tegra_pcie_power_on(struct tegra_pcie *pcie) { + struct device *dev = pcie->dev; const struct tegra_pcie_soc *soc = pcie->soc; int err; @@ -1082,13 +1087,13 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie) /* enable regulators */ err = regulator_bulk_enable(pcie->num_supplies, pcie->supplies); if (err < 0) - dev_err(pcie->dev, "failed to enable regulators: %d\n", err); + dev_err(dev, "failed to enable regulators: %d\n", err); err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, pcie->pex_clk, pcie->pex_rst); if (err) { - dev_err(pcie->dev, "powerup sequence failed: %d\n", err); + dev_err(dev, "powerup sequence failed: %d\n", err); return err; } @@ -1096,22 +1101,21 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie) err = clk_prepare_enable(pcie->afi_clk); if (err < 0) { - dev_err(pcie->dev, "failed to enable AFI clock: %d\n", err); + dev_err(dev, "failed to enable AFI clock: %d\n", err); return err; } if (soc->has_cml_clk) { err = clk_prepare_enable(pcie->cml_clk); if (err < 0) { - dev_err(pcie->dev, "failed to enable CML clock: %d\n", - err); + dev_err(dev, "failed to enable CML clock: %d\n", err); return err; } } err = clk_prepare_enable(pcie->pll_e); if (err < 0) { - dev_err(pcie->dev, "failed to enable PLLE clock: %d\n", err); + dev_err(dev, "failed to enable PLLE clock: %d\n", err); return err; } @@ -1120,22 +1124,23 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie) static int tegra_pcie_clocks_get(struct tegra_pcie *pcie) { + struct device *dev = pcie->dev; const struct tegra_pcie_soc *soc = pcie->soc; - pcie->pex_clk = devm_clk_get(pcie->dev, "pex"); + pcie->pex_clk = devm_clk_get(dev, "pex"); if (IS_ERR(pcie->pex_clk)) return PTR_ERR(pcie->pex_clk); - pcie->afi_clk = devm_clk_get(pcie->dev, "afi"); + pcie->afi_clk = devm_clk_get(dev, "afi"); if (IS_ERR(pcie->afi_clk)) return PTR_ERR(pcie->afi_clk); - pcie->pll_e = devm_clk_get(pcie->dev, "pll_e"); + pcie->pll_e = devm_clk_get(dev, "pll_e"); if (IS_ERR(pcie->pll_e)) return PTR_ERR(pcie->pll_e); if (soc->has_cml_clk) { - pcie->cml_clk = devm_clk_get(pcie->dev, "cml"); + pcie->cml_clk = devm_clk_get(dev, "cml"); if (IS_ERR(pcie->cml_clk)) return PTR_ERR(pcie->cml_clk); } @@ -1145,15 +1150,17 @@ static int tegra_pcie_clocks_get(struct tegra_pcie *pcie) static int tegra_pcie_resets_get(struct tegra_pcie *pcie) { - pcie->pex_rst = devm_reset_control_get(pcie->dev, "pex"); + struct device *dev = pcie->dev; + + pcie->pex_rst = devm_reset_control_get(dev, "pex"); if (IS_ERR(pcie->pex_rst)) return PTR_ERR(pcie->pex_rst); - pcie->afi_rst = devm_reset_control_get(pcie->dev, "afi"); + pcie->afi_rst = devm_reset_control_get(dev, "afi"); if (IS_ERR(pcie->afi_rst)) return PTR_ERR(pcie->afi_rst); - pcie->pcie_xrst = devm_reset_control_get(pcie->dev, "pcie_x"); + pcie->pcie_xrst = devm_reset_control_get(dev, "pcie_x"); if (IS_ERR(pcie->pcie_xrst)) return PTR_ERR(pcie->pcie_xrst); @@ -1162,18 +1169,19 @@ static int tegra_pcie_resets_get(struct tegra_pcie *pcie) static int tegra_pcie_phys_get_legacy(struct tegra_pcie *pcie) { + struct device *dev = pcie->dev; int err; - pcie->phy = devm_phy_optional_get(pcie->dev, "pcie"); + pcie->phy = devm_phy_optional_get(dev, "pcie"); if (IS_ERR(pcie->phy)) { err = PTR_ERR(pcie->phy); - dev_err(pcie->dev, "failed to get PHY: %d\n", err); + dev_err(dev, "failed to get PHY: %d\n", err); return err; } err = phy_init(pcie->phy); if (err < 0) { - dev_err(pcie->dev, "failed to initialize PHY: %d\n", err); + dev_err(dev, "failed to initialize PHY: %d\n", err); return err; } @@ -1256,43 +1264,44 @@ static int tegra_pcie_phys_get(struct tegra_pcie *pcie) static int tegra_pcie_get_resources(struct tegra_pcie *pcie) { - struct platform_device *pdev = to_platform_device(pcie->dev); + struct device *dev = pcie->dev; + struct platform_device *pdev = to_platform_device(dev); struct resource *pads, *afi, *res; int err; err = tegra_pcie_clocks_get(pcie); if (err) { - dev_err(&pdev->dev, "failed to get clocks: %d\n", err); + dev_err(dev, "failed to get clocks: %d\n", err); return err; } err = tegra_pcie_resets_get(pcie); if (err) { - dev_err(&pdev->dev, "failed to get resets: %d\n", err); + dev_err(dev, "failed to get resets: %d\n", err); return err; } err = tegra_pcie_phys_get(pcie); if (err < 0) { - dev_err(&pdev->dev, "failed to get PHYs: %d\n", err); + dev_err(dev, "failed to get PHYs: %d\n", err); return err; } err = tegra_pcie_power_on(pcie); if (err) { - dev_err(&pdev->dev, "failed to power up: %d\n", err); + dev_err(dev, "failed to power up: %d\n", err); return err; } pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads"); - pcie->pads = devm_ioremap_resource(&pdev->dev, pads); + pcie->pads = devm_ioremap_resource(dev, pads); if (IS_ERR(pcie->pads)) { err = PTR_ERR(pcie->pads); goto poweroff; } afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi"); - pcie->afi = devm_ioremap_resource(&pdev->dev, afi); + pcie->afi = devm_ioremap_resource(dev, afi); if (IS_ERR(pcie->afi)) { err = PTR_ERR(pcie->afi); goto poweroff; @@ -1305,7 +1314,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) goto poweroff; } - pcie->cs = devm_request_mem_region(pcie->dev, res->start, + pcie->cs = devm_request_mem_region(dev, res->start, resource_size(res), res->name); if (!pcie->cs) { err = -EADDRNOTAVAIL; @@ -1315,7 +1324,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) /* request interrupt */ err = platform_get_irq_byname(pdev, "intr"); if (err < 0) { - dev_err(&pdev->dev, "failed to get IRQ: %d\n", err); + dev_err(dev, "failed to get IRQ: %d\n", err); goto poweroff; } @@ -1323,7 +1332,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie); if (err) { - dev_err(&pdev->dev, "failed to register IRQ: %d\n", err); + dev_err(dev, "failed to register IRQ: %d\n", err); goto poweroff; } @@ -1336,6 +1345,7 @@ poweroff: static int tegra_pcie_put_resources(struct tegra_pcie *pcie) { + struct device *dev = pcie->dev; int err; if (pcie->irq > 0) @@ -1345,7 +1355,7 @@ static int tegra_pcie_put_resources(struct tegra_pcie *pcie) err = phy_exit(pcie->phy); if (err < 0) - dev_err(pcie->dev, "failed to teardown PHY: %d\n", err); + dev_err(dev, "failed to teardown PHY: %d\n", err); return 0; } @@ -1384,6 +1394,7 @@ static void tegra_msi_free(struct tegra_msi *chip, unsigned long irq) static irqreturn_t tegra_pcie_msi_irq(int irq, void *data) { struct tegra_pcie *pcie = data; + struct device *dev = pcie->dev; struct tegra_msi *msi = &pcie->msi; unsigned int i, processed = 0; @@ -1403,13 +1414,13 @@ static irqreturn_t tegra_pcie_msi_irq(int irq, void *data) if (test_bit(index, msi->used)) generic_handle_irq(irq); else - dev_info(pcie->dev, "unhandled MSI\n"); + dev_info(dev, "unhandled MSI\n"); } else { /* * that's weird who triggered this? * just clear it */ - dev_info(pcie->dev, "unexpected MSI\n"); + dev_info(dev, "unexpected MSI\n"); } /* see if there's any more pending in this vector */ @@ -1488,7 +1499,8 @@ static const struct irq_domain_ops msi_domain_ops = { static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) { - struct platform_device *pdev = to_platform_device(pcie->dev); + struct device *dev = pcie->dev; + struct platform_device *pdev = to_platform_device(dev); const struct tegra_pcie_soc *soc = pcie->soc; struct tegra_msi *msi = &pcie->msi; unsigned long base; @@ -1497,20 +1509,20 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) mutex_init(&msi->lock); - msi->chip.dev = pcie->dev; + msi->chip.dev = dev; msi->chip.setup_irq = tegra_msi_setup_irq; msi->chip.teardown_irq = tegra_msi_teardown_irq; - msi->domain = irq_domain_add_linear(pcie->dev->of_node, INT_PCI_MSI_NR, + msi->domain = irq_domain_add_linear(dev->of_node, INT_PCI_MSI_NR, &msi_domain_ops, &msi->chip); if (!msi->domain) { - dev_err(&pdev->dev, "failed to create IRQ domain\n"); + dev_err(dev, "failed to create IRQ domain\n"); return -ENOMEM; } err = platform_get_irq_byname(pdev, "msi"); if (err < 0) { - dev_err(&pdev->dev, "failed to get IRQ: %d\n", err); + dev_err(dev, "failed to get IRQ: %d\n", err); goto err; } @@ -1519,7 +1531,7 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie) err = request_irq(msi->irq, tegra_pcie_msi_irq, IRQF_NO_THREAD, tegra_msi_irq_chip.name, pcie); if (err < 0) { - dev_err(&pdev->dev, "failed to request IRQ: %d\n", err); + dev_err(dev, "failed to request IRQ: %d\n", err); goto err; } @@ -1594,46 +1606,47 @@ static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes, u32 *xbar) { - struct device_node *np = pcie->dev->of_node; + struct device *dev = pcie->dev; + struct device_node *np = dev->of_node; if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) { switch (lanes) { case 0x0000104: - dev_info(pcie->dev, "4x1, 1x1 configuration\n"); + dev_info(dev, "4x1, 1x1 configuration\n"); *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1; return 0; case 0x0000102: - dev_info(pcie->dev, "2x1, 1x1 configuration\n"); + dev_info(dev, "2x1, 1x1 configuration\n"); *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1; return 0; } } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) { switch (lanes) { case 0x00000204: - dev_info(pcie->dev, "4x1, 2x1 configuration\n"); + dev_info(dev, "4x1, 2x1 configuration\n"); *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420; return 0; case 0x00020202: - dev_info(pcie->dev, "2x3 configuration\n"); + dev_info(dev, "2x3 configuration\n"); *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222; return 0; case 0x00010104: - dev_info(pcie->dev, "4x1, 1x2 configuration\n"); + dev_info(dev, "4x1, 1x2 configuration\n"); *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411; return 0; } } else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) { switch (lanes) { case 0x00000004: - dev_info(pcie->dev, "single-mode configuration\n"); + dev_info(dev, "single-mode configuration\n"); *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE; return 0; case 0x00000202: - dev_info(pcie->dev, "dual-mode configuration\n"); + dev_info(dev, "dual-mode configuration\n"); *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL; return 0; } @@ -1673,7 +1686,8 @@ static bool of_regulator_bulk_available(struct device_node *np, */ static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie) { - struct device_node *np = pcie->dev->of_node; + struct device *dev = pcie->dev; + struct device_node *np = dev->of_node; if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) pcie->num_supplies = 3; @@ -1681,12 +1695,12 @@ static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie) pcie->num_supplies = 2; if (pcie->num_supplies == 0) { - dev_err(pcie->dev, "device %s not supported in legacy mode\n", + dev_err(dev, "device %s not supported in legacy mode\n", np->full_name); return -ENODEV; } - pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies, + pcie->supplies = devm_kcalloc(dev, pcie->num_supplies, sizeof(*pcie->supplies), GFP_KERNEL); if (!pcie->supplies) @@ -1698,8 +1712,7 @@ static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie) if (pcie->num_supplies > 2) pcie->supplies[2].supply = "avdd"; - return devm_regulator_bulk_get(pcie->dev, pcie->num_supplies, - pcie->supplies); + return devm_regulator_bulk_get(dev, pcie->num_supplies, pcie->supplies); } /* @@ -1713,13 +1726,14 @@ static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie) */ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) { - struct device_node *np = pcie->dev->of_node; + struct device *dev = pcie->dev; + struct device_node *np = dev->of_node; unsigned int i = 0; if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) { pcie->num_supplies = 7; - pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies, + pcie->supplies = devm_kcalloc(dev, pcie->num_supplies, sizeof(*pcie->supplies), GFP_KERNEL); if (!pcie->supplies) @@ -1746,7 +1760,7 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) pcie->num_supplies = 4 + (need_pexa ? 2 : 0) + (need_pexb ? 2 : 0); - pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies, + pcie->supplies = devm_kcalloc(dev, pcie->num_supplies, sizeof(*pcie->supplies), GFP_KERNEL); if (!pcie->supplies) @@ -1769,7 +1783,7 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) } else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) { pcie->num_supplies = 5; - pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies, + pcie->supplies = devm_kcalloc(dev, pcie->num_supplies, sizeof(*pcie->supplies), GFP_KERNEL); if (!pcie->supplies) @@ -1782,9 +1796,9 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) pcie->supplies[4].supply = "vddio-pex-clk"; } - if (of_regulator_bulk_available(pcie->dev->of_node, pcie->supplies, + if (of_regulator_bulk_available(dev->of_node, pcie->supplies, pcie->num_supplies)) - return devm_regulator_bulk_get(pcie->dev, pcie->num_supplies, + return devm_regulator_bulk_get(dev, pcie->num_supplies, pcie->supplies); /* @@ -1792,9 +1806,9 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) * that the device tree complies with an older version of the device * tree binding. */ - dev_info(pcie->dev, "using legacy DT binding for power supplies\n"); + dev_info(dev, "using legacy DT binding for power supplies\n"); - devm_kfree(pcie->dev, pcie->supplies); + devm_kfree(dev, pcie->supplies); pcie->num_supplies = 0; return tegra_pcie_get_legacy_regulators(pcie); @@ -1802,7 +1816,8 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) { - struct device_node *np = pcie->dev->of_node, *port; + struct device *dev = pcie->dev; + struct device_node *np = dev->of_node, *port; const struct tegra_pcie_soc *soc = pcie->soc; struct of_pci_range_parser parser; struct of_pci_range range; @@ -1812,7 +1827,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) int err; if (of_pci_range_parser_init(&parser, np)) { - dev_err(pcie->dev, "missing \"ranges\" property\n"); + dev_err(dev, "missing \"ranges\" property\n"); return -EINVAL; } @@ -1867,8 +1882,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) err = of_pci_parse_bus_range(np, &pcie->busn); if (err < 0) { - dev_err(pcie->dev, "failed to parse ranges property: %d\n", - err); + dev_err(dev, "failed to parse ranges property: %d\n", err); pcie->busn.name = np->name; pcie->busn.start = 0; pcie->busn.end = 0xff; @@ -1883,15 +1897,14 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) err = of_pci_get_devfn(port); if (err < 0) { - dev_err(pcie->dev, "failed to parse address: %d\n", - err); + dev_err(dev, "failed to parse address: %d\n", err); return err; } index = PCI_SLOT(err); if (index < 1 || index > soc->num_ports) { - dev_err(pcie->dev, "invalid port number: %d\n", index); + dev_err(dev, "invalid port number: %d\n", index); return -EINVAL; } @@ -1899,13 +1912,13 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) err = of_property_read_u32(port, "nvidia,num-lanes", &value); if (err < 0) { - dev_err(pcie->dev, "failed to parse # of lanes: %d\n", + dev_err(dev, "failed to parse # of lanes: %d\n", err); return err; } if (value > 16) { - dev_err(pcie->dev, "invalid # of lanes: %u\n", value); + dev_err(dev, "invalid # of lanes: %u\n", value); return -EINVAL; } @@ -1919,14 +1932,13 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) mask |= ((1 << value) - 1) << lane; lane += value; - rp = devm_kzalloc(pcie->dev, sizeof(*rp), GFP_KERNEL); + rp = devm_kzalloc(dev, sizeof(*rp), GFP_KERNEL); if (!rp) return -ENOMEM; err = of_address_to_resource(port, 0, &rp->regs); if (err < 0) { - dev_err(pcie->dev, "failed to parse address: %d\n", - err); + dev_err(dev, "failed to parse address: %d\n", err); return err; } @@ -1936,7 +1948,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) rp->pcie = pcie; rp->np = port; - rp->base = devm_ioremap_resource(pcie->dev, &rp->regs); + rp->base = devm_ioremap_resource(dev, &rp->regs); if (IS_ERR(rp->base)) return PTR_ERR(rp->base); @@ -1945,7 +1957,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) err = tegra_pcie_get_xbar_config(pcie, lanes, &pcie->xbar_config); if (err < 0) { - dev_err(pcie->dev, "invalid lane configuration\n"); + dev_err(dev, "invalid lane configuration\n"); return err; } @@ -1964,6 +1976,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) #define TEGRA_PCIE_LINKUP_TIMEOUT 200 /* up to 1.2 seconds */ static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port) { + struct device *dev = port->pcie->dev; unsigned int retries = 3; unsigned long value; @@ -1986,8 +1999,7 @@ static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port) } while (--timeout); if (!timeout) { - dev_err(port->pcie->dev, "link %u down, retrying\n", - port->index); + dev_err(dev, "link %u down, retrying\n", port->index); goto retry; } @@ -2011,11 +2023,12 @@ retry: static int tegra_pcie_enable(struct tegra_pcie *pcie) { + struct device *dev = pcie->dev; struct tegra_pcie_port *port, *tmp; struct hw_pci hw; list_for_each_entry_safe(port, tmp, &pcie->ports, list) { - dev_info(pcie->dev, "probing port %u, using %u lanes\n", + dev_info(dev, "probing port %u, using %u lanes\n", port->index, port->lanes); tegra_pcie_port_enable(port); @@ -2023,7 +2036,7 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie) if (tegra_pcie_port_check_link(port)) continue; - dev_info(pcie->dev, "link %u down, ignoring\n", port->index); + dev_info(dev, "link %u down, ignoring\n", port->index); tegra_pcie_port_disable(port); tegra_pcie_port_free(port); @@ -2041,8 +2054,7 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie) hw.map_irq = tegra_pcie_map_irq; hw.ops = &tegra_pcie_ops; - pci_common_init_dev(pcie->dev, &hw); - + pci_common_init_dev(dev, &hw); return 0; } @@ -2204,17 +2216,18 @@ remove: static int tegra_pcie_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct tegra_pcie *pcie; int err; - pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) return -ENOMEM; - pcie->soc = of_device_get_match_data(&pdev->dev); + pcie->soc = of_device_get_match_data(dev); INIT_LIST_HEAD(&pcie->buses); INIT_LIST_HEAD(&pcie->ports); - pcie->dev = &pdev->dev; + pcie->dev = dev; err = tegra_pcie_parse_dt(pcie); if (err < 0) @@ -2222,7 +2235,7 @@ static int tegra_pcie_probe(struct platform_device *pdev) err = tegra_pcie_get_resources(pcie); if (err < 0) { - dev_err(&pdev->dev, "failed to request resources: %d\n", err); + dev_err(dev, "failed to request resources: %d\n", err); return err; } @@ -2236,27 +2249,23 @@ static int tegra_pcie_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_PCI_MSI)) { err = tegra_pcie_enable_msi(pcie); if (err < 0) { - dev_err(&pdev->dev, - "failed to enable MSI support: %d\n", - err); + dev_err(dev, "failed to enable MSI support: %d\n", err); goto put_resources; } } err = tegra_pcie_enable(pcie); if (err < 0) { - dev_err(&pdev->dev, "failed to enable PCIe ports: %d\n", err); + dev_err(dev, "failed to enable PCIe ports: %d\n", err); goto disable_msi; } if (IS_ENABLED(CONFIG_DEBUG_FS)) { err = tegra_pcie_debugfs_init(pcie); if (err < 0) - dev_err(&pdev->dev, "failed to setup debugfs: %d\n", - err); + dev_err(dev, "failed to setup debugfs: %d\n", err); } - platform_set_drvdata(pdev, pcie); return 0; disable_msi: diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c index a81273c23341..1de23d74783f 100644 --- a/drivers/pci/host/pci-xgene.c +++ b/drivers/pci/host/pci-xgene.c @@ -76,6 +76,16 @@ struct xgene_pcie_port { u32 version; }; +static u32 xgene_pcie_readl(struct xgene_pcie_port *port, u32 reg) +{ + return readl(port->csr_base + reg); +} + +static void xgene_pcie_writel(struct xgene_pcie_port *port, u32 reg, u32 val) +{ + writel(val, port->csr_base + reg); +} + static inline u32 pcie_bar_low_val(u32 addr, u32 flags) { return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags; @@ -112,9 +122,9 @@ static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn) if (!pci_is_root_bus(bus)) rtdid_val = (b << 8) | (d << 3) | f; - writel(rtdid_val, port->csr_base + RTDID); + xgene_pcie_writel(port, RTDID, rtdid_val); /* read the register back to ensure flush */ - readl(port->csr_base + RTDID); + xgene_pcie_readl(port, RTDID); } /* @@ -179,28 +189,28 @@ static struct pci_ops xgene_pcie_ops = { .write = pci_generic_config_write32, }; -static u64 xgene_pcie_set_ib_mask(void __iomem *csr_base, u32 addr, +static u64 xgene_pcie_set_ib_mask(struct xgene_pcie_port *port, u32 addr, u32 flags, u64 size) { u64 mask = (~(size - 1) & PCI_BASE_ADDRESS_MEM_MASK) | flags; u32 val32 = 0; u32 val; - val32 = readl(csr_base + addr); + val32 = xgene_pcie_readl(port, addr); val = (val32 & 0x0000ffff) | (lower_32_bits(mask) << 16); - writel(val, csr_base + addr); + xgene_pcie_writel(port, addr, val); - val32 = readl(csr_base + addr + 0x04); + val32 = xgene_pcie_readl(port, addr + 0x04); val = (val32 & 0xffff0000) | (lower_32_bits(mask) >> 16); - writel(val, csr_base + addr + 0x04); + xgene_pcie_writel(port, addr + 0x04, val); - val32 = readl(csr_base + addr + 0x04); + val32 = xgene_pcie_readl(port, addr + 0x04); val = (val32 & 0x0000ffff) | (upper_32_bits(mask) << 16); - writel(val, csr_base + addr + 0x04); + xgene_pcie_writel(port, addr + 0x04, val); - val32 = readl(csr_base + addr + 0x08); + val32 = xgene_pcie_readl(port, addr + 0x08); val = (val32 & 0xffff0000) | (upper_32_bits(mask) >> 16); - writel(val, csr_base + addr + 0x08); + xgene_pcie_writel(port, addr + 0x08, val); return mask; } @@ -208,32 +218,32 @@ static u64 xgene_pcie_set_ib_mask(void __iomem *csr_base, u32 addr, static void xgene_pcie_linkup(struct xgene_pcie_port *port, u32 *lanes, u32 *speed) { - void __iomem *csr_base = port->csr_base; u32 val32; port->link_up = false; - val32 = readl(csr_base + PCIECORE_CTLANDSTATUS); + val32 = xgene_pcie_readl(port, PCIECORE_CTLANDSTATUS); if (val32 & LINK_UP_MASK) { port->link_up = true; *speed = PIPE_PHY_RATE_RD(val32); - val32 = readl(csr_base + BRIDGE_STATUS_0); + val32 = xgene_pcie_readl(port, BRIDGE_STATUS_0); *lanes = val32 >> 26; } } static int xgene_pcie_init_port(struct xgene_pcie_port *port) { + struct device *dev = port->dev; int rc; - port->clk = clk_get(port->dev, NULL); + port->clk = clk_get(dev, NULL); if (IS_ERR(port->clk)) { - dev_err(port->dev, "clock not available\n"); + dev_err(dev, "clock not available\n"); return -ENODEV; } rc = clk_prepare_enable(port->clk); if (rc) { - dev_err(port->dev, "clock enable failed\n"); + dev_err(dev, "clock enable failed\n"); return rc; } @@ -243,15 +253,16 @@ static int xgene_pcie_init_port(struct xgene_pcie_port *port) static int xgene_pcie_map_reg(struct xgene_pcie_port *port, struct platform_device *pdev) { + struct device *dev = port->dev; struct resource *res; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr"); - port->csr_base = devm_ioremap_resource(port->dev, res); + port->csr_base = devm_ioremap_resource(dev, res); if (IS_ERR(port->csr_base)) return PTR_ERR(port->csr_base); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); - port->cfg_base = devm_ioremap_resource(port->dev, res); + port->cfg_base = devm_ioremap_resource(dev, res); if (IS_ERR(port->cfg_base)) return PTR_ERR(port->cfg_base); port->cfg_addr = res->start; @@ -263,7 +274,7 @@ static void xgene_pcie_setup_ob_reg(struct xgene_pcie_port *port, struct resource *res, u32 offset, u64 cpu_addr, u64 pci_addr) { - void __iomem *base = port->csr_base + offset; + struct device *dev = port->dev; resource_size_t size = resource_size(res); u64 restype = resource_type(res); u64 mask = 0; @@ -280,22 +291,24 @@ static void xgene_pcie_setup_ob_reg(struct xgene_pcie_port *port, if (size >= min_size) mask = ~(size - 1) | flag; else - dev_warn(port->dev, "res size 0x%llx less than minimum 0x%x\n", + dev_warn(dev, "res size 0x%llx less than minimum 0x%x\n", (u64)size, min_size); - writel(lower_32_bits(cpu_addr), base); - writel(upper_32_bits(cpu_addr), base + 0x04); - writel(lower_32_bits(mask), base + 0x08); - writel(upper_32_bits(mask), base + 0x0c); - writel(lower_32_bits(pci_addr), base + 0x10); - writel(upper_32_bits(pci_addr), base + 0x14); + xgene_pcie_writel(port, offset, lower_32_bits(cpu_addr)); + xgene_pcie_writel(port, offset + 0x04, upper_32_bits(cpu_addr)); + xgene_pcie_writel(port, offset + 0x08, lower_32_bits(mask)); + xgene_pcie_writel(port, offset + 0x0c, upper_32_bits(mask)); + xgene_pcie_writel(port, offset + 0x10, lower_32_bits(pci_addr)); + xgene_pcie_writel(port, offset + 0x14, upper_32_bits(pci_addr)); } -static void xgene_pcie_setup_cfg_reg(void __iomem *csr_base, u64 addr) +static void xgene_pcie_setup_cfg_reg(struct xgene_pcie_port *port) { - writel(lower_32_bits(addr), csr_base + CFGBARL); - writel(upper_32_bits(addr), csr_base + CFGBARH); - writel(EN_REG, csr_base + CFGCTL); + u64 addr = port->cfg_addr; + + xgene_pcie_writel(port, CFGBARL, lower_32_bits(addr)); + xgene_pcie_writel(port, CFGBARH, upper_32_bits(addr)); + xgene_pcie_writel(port, CFGCTL, EN_REG); } static int xgene_pcie_map_ranges(struct xgene_pcie_port *port, @@ -310,7 +323,7 @@ static int xgene_pcie_map_ranges(struct xgene_pcie_port *port, struct resource *res = window->res; u64 restype = resource_type(res); - dev_dbg(port->dev, "%pR\n", res); + dev_dbg(dev, "%pR\n", res); switch (restype) { case IORESOURCE_IO: @@ -339,17 +352,18 @@ static int xgene_pcie_map_ranges(struct xgene_pcie_port *port, return -EINVAL; } } - xgene_pcie_setup_cfg_reg(port->csr_base, port->cfg_addr); - + xgene_pcie_setup_cfg_reg(port); return 0; } -static void xgene_pcie_setup_pims(void *addr, u64 pim, u64 size) +static void xgene_pcie_setup_pims(struct xgene_pcie_port *port, u32 pim_reg, + u64 pim, u64 size) { - writel(lower_32_bits(pim), addr); - writel(upper_32_bits(pim) | EN_COHERENCY, addr + 0x04); - writel(lower_32_bits(size), addr + 0x10); - writel(upper_32_bits(size), addr + 0x14); + xgene_pcie_writel(port, pim_reg, lower_32_bits(pim)); + xgene_pcie_writel(port, pim_reg + 0x04, + upper_32_bits(pim) | EN_COHERENCY); + xgene_pcie_writel(port, pim_reg + 0x10, lower_32_bits(size)); + xgene_pcie_writel(port, pim_reg + 0x14, upper_32_bits(size)); } /* @@ -379,10 +393,10 @@ static int xgene_pcie_select_ib_reg(u8 *ib_reg_mask, u64 size) static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port, struct of_pci_range *range, u8 *ib_reg_mask) { - void __iomem *csr_base = port->csr_base; void __iomem *cfg_base = port->cfg_base; + struct device *dev = port->dev; void *bar_addr; - void *pim_addr; + u32 pim_reg; u64 cpu_addr = range->cpu_addr; u64 pci_addr = range->pci_addr; u64 size = range->size; @@ -393,7 +407,7 @@ static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port, region = xgene_pcie_select_ib_reg(ib_reg_mask, range->size); if (region < 0) { - dev_warn(port->dev, "invalid pcie dma-range config\n"); + dev_warn(dev, "invalid pcie dma-range config\n"); return; } @@ -403,29 +417,27 @@ static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port, bar_low = pcie_bar_low_val((u32)cpu_addr, flags); switch (region) { case 0: - xgene_pcie_set_ib_mask(csr_base, BRIDGE_CFG_4, flags, size); + xgene_pcie_set_ib_mask(port, BRIDGE_CFG_4, flags, size); bar_addr = cfg_base + PCI_BASE_ADDRESS_0; writel(bar_low, bar_addr); writel(upper_32_bits(cpu_addr), bar_addr + 0x4); - pim_addr = csr_base + PIM1_1L; + pim_reg = PIM1_1L; break; case 1: - bar_addr = csr_base + IBAR2; - writel(bar_low, bar_addr); - writel(lower_32_bits(mask), csr_base + IR2MSK); - pim_addr = csr_base + PIM2_1L; + xgene_pcie_writel(port, IBAR2, bar_low); + xgene_pcie_writel(port, IR2MSK, lower_32_bits(mask)); + pim_reg = PIM2_1L; break; case 2: - bar_addr = csr_base + IBAR3L; - writel(bar_low, bar_addr); - writel(upper_32_bits(cpu_addr), bar_addr + 0x4); - writel(lower_32_bits(mask), csr_base + IR3MSKL); - writel(upper_32_bits(mask), csr_base + IR3MSKL + 0x4); - pim_addr = csr_base + PIM3_1L; + xgene_pcie_writel(port, IBAR3L, bar_low); + xgene_pcie_writel(port, IBAR3L + 0x4, upper_32_bits(cpu_addr)); + xgene_pcie_writel(port, IR3MSKL, lower_32_bits(mask)); + xgene_pcie_writel(port, IR3MSKL + 0x4, upper_32_bits(mask)); + pim_reg = PIM3_1L; break; } - xgene_pcie_setup_pims(pim_addr, pci_addr, ~(size - 1)); + xgene_pcie_setup_pims(port, pim_reg, pci_addr, ~(size - 1)); } static int pci_dma_range_parser_init(struct of_pci_range_parser *parser, @@ -463,7 +475,7 @@ static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie_port *port) for_each_of_pci_range(&parser, &range) { u64 end = range.cpu_addr + range.size - 1; - dev_dbg(port->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n", + dev_dbg(dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n", range.flags, range.cpu_addr, end, range.pci_addr); xgene_pcie_setup_ib_reg(port, &range, &ib_reg_mask); } @@ -476,13 +488,14 @@ static void xgene_pcie_clear_config(struct xgene_pcie_port *port) int i; for (i = PIM1_1L; i <= CFGCTL; i += 4) - writel(0x0, port->csr_base + i); + xgene_pcie_writel(port, i, 0); } static int xgene_pcie_setup(struct xgene_pcie_port *port, struct list_head *res, resource_size_t io_base) { + struct device *dev = port->dev; u32 val, lanes = 0, speed = 0; int ret; @@ -490,7 +503,7 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port, /* setup the vendor and device IDs correctly */ val = (XGENE_PCIE_DEVICEID << 16) | XGENE_PCIE_VENDORID; - writel(val, port->csr_base + BRIDGE_CFG_0); + xgene_pcie_writel(port, BRIDGE_CFG_0, val); ret = xgene_pcie_map_ranges(port, res, io_base); if (ret) @@ -502,27 +515,28 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port, xgene_pcie_linkup(port, &lanes, &speed); if (!port->link_up) - dev_info(port->dev, "(rc) link down\n"); + dev_info(dev, "(rc) link down\n"); else - dev_info(port->dev, "(rc) x%d gen-%d link up\n", - lanes, speed + 1); + dev_info(dev, "(rc) x%d gen-%d link up\n", lanes, speed + 1); return 0; } static int xgene_pcie_probe_bridge(struct platform_device *pdev) { - struct device_node *dn = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node; struct xgene_pcie_port *port; resource_size_t iobase = 0; struct pci_bus *bus; int ret; LIST_HEAD(res); - port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); if (!port) return -ENOMEM; - port->node = of_node_get(pdev->dev.of_node); - port->dev = &pdev->dev; + + port->node = of_node_get(dn); + port->dev = dev; port->version = XGENE_PCIE_IP_VER_UNKN; if (of_device_is_compatible(port->node, "apm,xgene-pcie")) @@ -540,7 +554,7 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev) if (ret) return ret; - ret = devm_request_pci_bus_resources(&pdev->dev, &res); + ret = devm_request_pci_bus_resources(dev, &res); if (ret) goto error; @@ -548,8 +562,7 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev) if (ret) goto error; - bus = pci_create_root_bus(&pdev->dev, 0, - &xgene_pcie_ops, port, &res); + bus = pci_create_root_bus(dev, 0, &xgene_pcie_ops, port, &res); if (!bus) { ret = -ENOMEM; goto error; @@ -558,8 +571,6 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev) pci_scan_child_bus(bus); pci_assign_unassigned_bus_resources(bus); pci_bus_add_devices(bus); - - platform_set_drvdata(pdev, port); return 0; error: diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c index c24e96559cbb..b0ac4dfafa0b 100644 --- a/drivers/pci/host/pcie-altera.c +++ b/drivers/pci/host/pcie-altera.c @@ -55,15 +55,19 @@ #define TLP_PAYLOAD_SIZE 0x01 #define TLP_READ_TAG 0x1d #define TLP_WRITE_TAG 0x10 -#define TLP_CFG_DW0(fmttype) (((fmttype) << 24) | TLP_PAYLOAD_SIZE) -#define TLP_CFG_DW1(reqid, tag, be) (((reqid) << 16) | (tag << 8) | (be)) +#define RP_DEVFN 0 +#define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn)) +#define TLP_CFG_DW0(pcie, bus) \ + ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGRD0 \ + : TLP_FMTTYPE_CFGRD1) << 24) | \ + TLP_PAYLOAD_SIZE) +#define TLP_CFG_DW1(pcie, tag, be) \ + (((TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be)) #define TLP_CFG_DW2(bus, devfn, offset) \ (((bus) << 24) | ((devfn) << 16) | (offset)) -#define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn)) #define TLP_COMP_STATUS(s) (((s) >> 12) & 7) #define TLP_HDR_SIZE 3 #define TLP_LOOP 500 -#define RP_DEVFN 0 #define LINK_UP_TIMEOUT HZ #define LINK_RETRAIN_TIMEOUT HZ @@ -74,7 +78,7 @@ struct altera_pcie { struct platform_device *pdev; - void __iomem *cra_base; + void __iomem *cra_base; /* DT Cra */ int irq; u8 root_bus_nr; struct irq_domain *irq_domain; @@ -131,7 +135,7 @@ static void tlp_write_tx(struct altera_pcie *pcie, cra_writel(pcie, tlp_rp_regdata->ctrl, RP_TX_CNTRL); } -static bool altera_pcie_valid_config(struct altera_pcie *pcie, +static bool altera_pcie_valid_device(struct altera_pcie *pcie, struct pci_bus *bus, int dev) { /* If there is no link, then there is no device */ @@ -218,13 +222,8 @@ static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn, { u32 headers[TLP_HDR_SIZE]; - if (bus == pcie->root_bus_nr) - headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD0); - else - headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD1); - - headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN), - TLP_READ_TAG, byte_en); + headers[0] = TLP_CFG_DW0(pcie, bus); + headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en); headers[2] = TLP_CFG_DW2(bus, devfn, where); tlp_write_packet(pcie, headers, 0, false); @@ -238,13 +237,8 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn, u32 headers[TLP_HDR_SIZE]; int ret; - if (bus == pcie->root_bus_nr) - headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR0); - else - headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR1); - - headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN), - TLP_WRITE_TAG, byte_en); + headers[0] = TLP_CFG_DW0(pcie, bus); + headers[1] = TLP_CFG_DW1(pcie, TLP_WRITE_TAG, byte_en); headers[2] = TLP_CFG_DW2(bus, devfn, where); /* check alignment to Qword */ @@ -342,7 +336,7 @@ static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn, if (altera_pcie_hide_rc_bar(bus, devfn, where)) return PCIBIOS_BAD_REGISTER_NUMBER; - if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) { + if (!altera_pcie_valid_device(pcie, bus, PCI_SLOT(devfn))) { *value = 0xffffffff; return PCIBIOS_DEVICE_NOT_FOUND; } @@ -359,7 +353,7 @@ static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn, if (altera_pcie_hide_rc_bar(bus, devfn, where)) return PCIBIOS_BAD_REGISTER_NUMBER; - if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) + if (!altera_pcie_valid_device(pcie, bus, PCI_SLOT(devfn))) return PCIBIOS_DEVICE_NOT_FOUND; return _altera_pcie_cfg_write(pcie, bus->number, devfn, where, size, @@ -394,6 +388,7 @@ static int altera_write_cap_word(struct altera_pcie *pcie, u8 busno, static void altera_wait_link_retrain(struct altera_pcie *pcie) { + struct device *dev = &pcie->pdev->dev; u16 reg16; unsigned long start_jiffies; @@ -406,7 +401,7 @@ static void altera_wait_link_retrain(struct altera_pcie *pcie) break; if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) { - dev_err(&pcie->pdev->dev, "link retrain timeout\n"); + dev_err(dev, "link retrain timeout\n"); break; } udelay(100); @@ -419,7 +414,7 @@ static void altera_wait_link_retrain(struct altera_pcie *pcie) break; if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) { - dev_err(&pcie->pdev->dev, "link up timeout\n"); + dev_err(dev, "link up timeout\n"); break; } udelay(100); @@ -460,7 +455,6 @@ static int altera_pcie_intx_map(struct irq_domain *domain, unsigned int irq, { irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); irq_set_chip_data(irq, domain->host_data); - return 0; } @@ -472,12 +466,14 @@ static void altera_pcie_isr(struct irq_desc *desc) { struct irq_chip *chip = irq_desc_get_chip(desc); struct altera_pcie *pcie; + struct device *dev; unsigned long status; u32 bit; u32 virq; chained_irq_enter(chip, desc); pcie = irq_desc_get_handler_data(desc); + dev = &pcie->pdev->dev; while ((status = cra_readl(pcie, P2A_INT_STATUS) & P2A_INT_STS_ALL) != 0) { @@ -489,8 +485,7 @@ static void altera_pcie_isr(struct irq_desc *desc) if (virq) generic_handle_irq(virq); else - dev_err(&pcie->pdev->dev, - "unexpected IRQ, INT%d\n", bit); + dev_err(dev, "unexpected IRQ, INT%d\n", bit); } } @@ -549,30 +544,25 @@ static int altera_pcie_init_irq_domain(struct altera_pcie *pcie) static int altera_pcie_parse_dt(struct altera_pcie *pcie) { - struct resource *cra; + struct device *dev = &pcie->pdev->dev; struct platform_device *pdev = pcie->pdev; + struct resource *cra; cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra"); - if (!cra) { - dev_err(&pdev->dev, "no Cra memory resource defined\n"); - return -ENODEV; - } - - pcie->cra_base = devm_ioremap_resource(&pdev->dev, cra); + pcie->cra_base = devm_ioremap_resource(dev, cra); if (IS_ERR(pcie->cra_base)) { - dev_err(&pdev->dev, "failed to map cra memory\n"); + dev_err(dev, "failed to map cra memory\n"); return PTR_ERR(pcie->cra_base); } /* setup IRQ */ pcie->irq = platform_get_irq(pdev, 0); if (pcie->irq <= 0) { - dev_err(&pdev->dev, "failed to get IRQ: %d\n", pcie->irq); + dev_err(dev, "failed to get IRQ: %d\n", pcie->irq); return -EINVAL; } irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie); - return 0; } @@ -583,12 +573,13 @@ static void altera_pcie_host_init(struct altera_pcie *pcie) static int altera_pcie_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct altera_pcie *pcie; struct pci_bus *bus; struct pci_bus *child; int ret; - pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) return -ENOMEM; @@ -596,7 +587,7 @@ static int altera_pcie_probe(struct platform_device *pdev) ret = altera_pcie_parse_dt(pcie); if (ret) { - dev_err(&pdev->dev, "Parsing DT failed\n"); + dev_err(dev, "Parsing DT failed\n"); return ret; } @@ -604,13 +595,13 @@ static int altera_pcie_probe(struct platform_device *pdev) ret = altera_pcie_parse_request_of_pci_ranges(pcie); if (ret) { - dev_err(&pdev->dev, "Failed add resources\n"); + dev_err(dev, "Failed add resources\n"); return ret; } ret = altera_pcie_init_irq_domain(pcie); if (ret) { - dev_err(&pdev->dev, "Failed creating IRQ Domain\n"); + dev_err(dev, "Failed creating IRQ Domain\n"); return ret; } @@ -620,7 +611,7 @@ static int altera_pcie_probe(struct platform_device *pdev) cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE); altera_pcie_host_init(pcie); - bus = pci_scan_root_bus(&pdev->dev, pcie->root_bus_nr, &altera_pcie_ops, + bus = pci_scan_root_bus(dev, pcie->root_bus_nr, &altera_pcie_ops, pcie, &pcie->resources); if (!bus) return -ENOMEM; @@ -633,8 +624,6 @@ static int altera_pcie_probe(struct platform_device *pdev) pcie_bus_configure_settings(child); pci_bus_add_devices(bus); - - platform_set_drvdata(pdev, pcie); return ret; } diff --git a/drivers/pci/host/pcie-armada8k.c b/drivers/pci/host/pcie-armada8k.c index 0f4f570068e3..0ac0f18690f2 100644 --- a/drivers/pci/host/pcie-armada8k.c +++ b/drivers/pci/host/pcie-armada8k.c @@ -29,34 +29,33 @@ #include "pcie-designware.h" struct armada8k_pcie { - void __iomem *base; + struct pcie_port pp; /* pp.dbi_base is DT ctrl */ struct clk *clk; - struct pcie_port pp; }; #define PCIE_VENDOR_REGS_OFFSET 0x8000 -#define PCIE_GLOBAL_CONTROL_REG 0x0 +#define PCIE_GLOBAL_CONTROL_REG (PCIE_VENDOR_REGS_OFFSET + 0x0) #define PCIE_APP_LTSSM_EN BIT(2) #define PCIE_DEVICE_TYPE_SHIFT 4 #define PCIE_DEVICE_TYPE_MASK 0xF #define PCIE_DEVICE_TYPE_RC 0x4 /* Root complex */ -#define PCIE_GLOBAL_STATUS_REG 0x8 +#define PCIE_GLOBAL_STATUS_REG (PCIE_VENDOR_REGS_OFFSET + 0x8) #define PCIE_GLB_STS_RDLH_LINK_UP BIT(1) #define PCIE_GLB_STS_PHY_LINK_UP BIT(9) -#define PCIE_GLOBAL_INT_CAUSE1_REG 0x1C -#define PCIE_GLOBAL_INT_MASK1_REG 0x20 +#define PCIE_GLOBAL_INT_CAUSE1_REG (PCIE_VENDOR_REGS_OFFSET + 0x1C) +#define PCIE_GLOBAL_INT_MASK1_REG (PCIE_VENDOR_REGS_OFFSET + 0x20) #define PCIE_INT_A_ASSERT_MASK BIT(9) #define PCIE_INT_B_ASSERT_MASK BIT(10) #define PCIE_INT_C_ASSERT_MASK BIT(11) #define PCIE_INT_D_ASSERT_MASK BIT(12) -#define PCIE_ARCACHE_TRC_REG 0x50 -#define PCIE_AWCACHE_TRC_REG 0x54 -#define PCIE_ARUSER_REG 0x5C -#define PCIE_AWUSER_REG 0x60 +#define PCIE_ARCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x50) +#define PCIE_AWCACHE_TRC_REG (PCIE_VENDOR_REGS_OFFSET + 0x54) +#define PCIE_ARUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x5C) +#define PCIE_AWUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x60) /* * AR/AW Cache defauls: Normal memory, Write-Back, Read / Write * allocate @@ -72,11 +71,10 @@ struct armada8k_pcie { static int armada8k_pcie_link_up(struct pcie_port *pp) { - struct armada8k_pcie *pcie = to_armada8k_pcie(pp); u32 reg; u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP; - reg = readl(pcie->base + PCIE_GLOBAL_STATUS_REG); + reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_STATUS_REG); if ((reg & mask) == mask) return 1; @@ -85,51 +83,50 @@ static int armada8k_pcie_link_up(struct pcie_port *pp) return 0; } -static void armada8k_pcie_establish_link(struct pcie_port *pp) +static void armada8k_pcie_establish_link(struct armada8k_pcie *pcie) { - struct armada8k_pcie *pcie = to_armada8k_pcie(pp); - void __iomem *base = pcie->base; + struct pcie_port *pp = &pcie->pp; u32 reg; if (!dw_pcie_link_up(pp)) { /* Disable LTSSM state machine to enable configuration */ - reg = readl(base + PCIE_GLOBAL_CONTROL_REG); + reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG); reg &= ~(PCIE_APP_LTSSM_EN); - writel(reg, base + PCIE_GLOBAL_CONTROL_REG); + dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg); } /* Set the device to root complex mode */ - reg = readl(base + PCIE_GLOBAL_CONTROL_REG); + reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG); reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT); reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT; - writel(reg, base + PCIE_GLOBAL_CONTROL_REG); + dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg); /* Set the PCIe master AxCache attributes */ - writel(ARCACHE_DEFAULT_VALUE, base + PCIE_ARCACHE_TRC_REG); - writel(AWCACHE_DEFAULT_VALUE, base + PCIE_AWCACHE_TRC_REG); + dw_pcie_writel_rc(pp, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE); + dw_pcie_writel_rc(pp, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE); /* Set the PCIe master AxDomain attributes */ - reg = readl(base + PCIE_ARUSER_REG); + reg = dw_pcie_readl_rc(pp, PCIE_ARUSER_REG); reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT); reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT; - writel(reg, base + PCIE_ARUSER_REG); + dw_pcie_writel_rc(pp, PCIE_ARUSER_REG, reg); - reg = readl(base + PCIE_AWUSER_REG); + reg = dw_pcie_readl_rc(pp, PCIE_AWUSER_REG); reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT); reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT; - writel(reg, base + PCIE_AWUSER_REG); + dw_pcie_writel_rc(pp, PCIE_AWUSER_REG, reg); /* Enable INT A-D interrupts */ - reg = readl(base + PCIE_GLOBAL_INT_MASK1_REG); + reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_INT_MASK1_REG); reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK | PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK; - writel(reg, base + PCIE_GLOBAL_INT_MASK1_REG); + dw_pcie_writel_rc(pp, PCIE_GLOBAL_INT_MASK1_REG, reg); if (!dw_pcie_link_up(pp)) { /* Configuration done. Start LTSSM */ - reg = readl(base + PCIE_GLOBAL_CONTROL_REG); + reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG); reg |= PCIE_APP_LTSSM_EN; - writel(reg, base + PCIE_GLOBAL_CONTROL_REG); + dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg); } /* Wait until the link becomes active again */ @@ -139,15 +136,16 @@ static void armada8k_pcie_establish_link(struct pcie_port *pp) static void armada8k_pcie_host_init(struct pcie_port *pp) { + struct armada8k_pcie *pcie = to_armada8k_pcie(pp); + dw_pcie_setup_rc(pp); - armada8k_pcie_establish_link(pp); + armada8k_pcie_establish_link(pcie); } static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg) { - struct pcie_port *pp = arg; - struct armada8k_pcie *pcie = to_armada8k_pcie(pp); - void __iomem *base = pcie->base; + struct armada8k_pcie *pcie = arg; + struct pcie_port *pp = &pcie->pp; u32 val; /* @@ -155,8 +153,8 @@ static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg) * PCI device. However, they are also latched into the PCIe * controller, so we simply discard them. */ - val = readl(base + PCIE_GLOBAL_INT_CAUSE1_REG); - writel(val, base + PCIE_GLOBAL_INT_CAUSE1_REG); + val = dw_pcie_readl_rc(pp, PCIE_GLOBAL_INT_CAUSE1_REG); + dw_pcie_writel_rc(pp, PCIE_GLOBAL_INT_CAUSE1_REG, val); return IRQ_HANDLED; } @@ -166,9 +164,10 @@ static struct pcie_host_ops armada8k_pcie_host_ops = { .host_init = armada8k_pcie_host_init, }; -static int armada8k_add_pcie_port(struct pcie_port *pp, +static int armada8k_add_pcie_port(struct armada8k_pcie *pcie, struct platform_device *pdev) { + struct pcie_port *pp = &pcie->pp; struct device *dev = &pdev->dev; int ret; @@ -182,7 +181,7 @@ static int armada8k_add_pcie_port(struct pcie_port *pp, } ret = devm_request_irq(dev, pp->irq, armada8k_pcie_irq_handler, - IRQF_SHARED, "armada8k-pcie", pp); + IRQF_SHARED, "armada8k-pcie", pcie); if (ret) { dev_err(dev, "failed to request irq %d\n", pp->irq); return ret; @@ -217,7 +216,6 @@ static int armada8k_pcie_probe(struct platform_device *pdev) pp = &pcie->pp; pp->dev = dev; - platform_set_drvdata(pdev, pcie); /* Get the dw-pcie unit configuration/control registers base. */ base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); @@ -228,9 +226,7 @@ static int armada8k_pcie_probe(struct platform_device *pdev) goto fail; } - pcie->base = pp->dbi_base + PCIE_VENDOR_REGS_OFFSET; - - ret = armada8k_add_pcie_port(pp, pdev); + ret = armada8k_add_pcie_port(pcie, pdev); if (ret) goto fail; diff --git a/drivers/pci/host/pcie-artpec6.c b/drivers/pci/host/pcie-artpec6.c index 39bf1a6df463..212786b27f1a 100644 --- a/drivers/pci/host/pcie-artpec6.c +++ b/drivers/pci/host/pcie-artpec6.c @@ -27,9 +27,9 @@ #define to_artpec6_pcie(x) container_of(x, struct artpec6_pcie, pp) struct artpec6_pcie { - struct pcie_port pp; - struct regmap *regmap; - void __iomem *phy_base; + struct pcie_port pp; /* pp.dbi_base is DT dbi */ + struct regmap *regmap; /* DT axis,syscon-pcie */ + void __iomem *phy_base; /* DT phy */ }; /* PCIe Port Logic registers (memory-mapped) */ @@ -65,18 +65,31 @@ struct artpec6_pcie { #define ARTPEC6_CPU_TO_BUS_ADDR 0x0fffffff -static int artpec6_pcie_establish_link(struct pcie_port *pp) +static u32 artpec6_pcie_readl(struct artpec6_pcie *artpec6_pcie, u32 offset) { - struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pp); + u32 val; + + regmap_read(artpec6_pcie->regmap, offset, &val); + return val; +} + +static void artpec6_pcie_writel(struct artpec6_pcie *artpec6_pcie, u32 offset, u32 val) +{ + regmap_write(artpec6_pcie->regmap, offset, val); +} + +static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie) +{ + struct pcie_port *pp = &artpec6_pcie->pp; u32 val; unsigned int retries; /* Hold DW core in reset */ - regmap_read(artpec6_pcie->regmap, PCIECFG, &val); + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); val |= PCIECFG_CORE_RESET_REQ; - regmap_write(artpec6_pcie->regmap, PCIECFG, val); + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); - regmap_read(artpec6_pcie->regmap, PCIECFG, &val); + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); val |= PCIECFG_RISRCREN | /* Receiver term. 50 Ohm */ PCIECFG_MODE_TX_DRV_EN | PCIECFG_CISRREN | /* Reference clock term. 100 Ohm */ @@ -84,27 +97,27 @@ static int artpec6_pcie_establish_link(struct pcie_port *pp) val |= PCIECFG_REFCLK_ENABLE; val &= ~PCIECFG_DBG_OEN; val &= ~PCIECFG_CLKREQ_B; - regmap_write(artpec6_pcie->regmap, PCIECFG, val); + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); usleep_range(5000, 6000); - regmap_read(artpec6_pcie->regmap, NOCCFG, &val); + val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); val |= NOCCFG_ENABLE_CLK_PCIE; - regmap_write(artpec6_pcie->regmap, NOCCFG, val); + artpec6_pcie_writel(artpec6_pcie, NOCCFG, val); usleep_range(20, 30); - regmap_read(artpec6_pcie->regmap, PCIECFG, &val); + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); val |= PCIECFG_PCLK_ENABLE | PCIECFG_PLL_ENABLE; - regmap_write(artpec6_pcie->regmap, PCIECFG, val); + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); usleep_range(6000, 7000); - regmap_read(artpec6_pcie->regmap, NOCCFG, &val); + val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); val &= ~NOCCFG_POWER_PCIE_IDLEREQ; - regmap_write(artpec6_pcie->regmap, NOCCFG, val); + artpec6_pcie_writel(artpec6_pcie, NOCCFG, val); retries = 50; do { usleep_range(1000, 2000); - regmap_read(artpec6_pcie->regmap, NOCCFG, &val); + val = artpec6_pcie_readl(artpec6_pcie, NOCCFG); retries--; } while (retries && (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE))); @@ -117,16 +130,16 @@ static int artpec6_pcie_establish_link(struct pcie_port *pp) } while (retries && !(val & PHY_COSPLLLOCK)); /* Take DW core out of reset */ - regmap_read(artpec6_pcie->regmap, PCIECFG, &val); + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); val &= ~PCIECFG_CORE_RESET_REQ; - regmap_write(artpec6_pcie->regmap, PCIECFG, val); + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); usleep_range(100, 200); /* * Enable writing to config regs. This is required as the Synopsys * driver changes the class code. That register needs DBI write enable. */ - writel(DBI_RO_WR_EN, pp->dbi_base + MISC_CONTROL_1_OFF); + dw_pcie_writel_rc(pp, MISC_CONTROL_1_OFF, DBI_RO_WR_EN); pp->io_base &= ARTPEC6_CPU_TO_BUS_ADDR; pp->mem_base &= ARTPEC6_CPU_TO_BUS_ADDR; @@ -137,78 +150,69 @@ static int artpec6_pcie_establish_link(struct pcie_port *pp) dw_pcie_setup_rc(pp); /* assert LTSSM enable */ - regmap_read(artpec6_pcie->regmap, PCIECFG, &val); + val = artpec6_pcie_readl(artpec6_pcie, PCIECFG); val |= PCIECFG_LTSSM_ENABLE; - regmap_write(artpec6_pcie->regmap, PCIECFG, val); + artpec6_pcie_writel(artpec6_pcie, PCIECFG, val); /* check if the link is up or not */ if (!dw_pcie_wait_for_link(pp)) return 0; dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", - readl(pp->dbi_base + PCIE_PHY_DEBUG_R0), - readl(pp->dbi_base + PCIE_PHY_DEBUG_R1)); + dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0), + dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1)); return -ETIMEDOUT; } -static void artpec6_pcie_enable_interrupts(struct pcie_port *pp) +static void artpec6_pcie_enable_interrupts(struct artpec6_pcie *artpec6_pcie) { + struct pcie_port *pp = &artpec6_pcie->pp; + if (IS_ENABLED(CONFIG_PCI_MSI)) dw_pcie_msi_init(pp); } static void artpec6_pcie_host_init(struct pcie_port *pp) { - artpec6_pcie_establish_link(pp); - artpec6_pcie_enable_interrupts(pp); -} - -static int artpec6_pcie_link_up(struct pcie_port *pp) -{ - u32 rc; - - /* - * Get status from Synopsys IP - * link is debug bit 36, debug register 1 starts at bit 32 - */ - rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) & (0x1 << (36 - 32)); - if (rc) - return 1; + struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pp); - return 0; + artpec6_pcie_establish_link(artpec6_pcie); + artpec6_pcie_enable_interrupts(artpec6_pcie); } static struct pcie_host_ops artpec6_pcie_host_ops = { - .link_up = artpec6_pcie_link_up, .host_init = artpec6_pcie_host_init, }; static irqreturn_t artpec6_pcie_msi_handler(int irq, void *arg) { - struct pcie_port *pp = arg; + struct artpec6_pcie *artpec6_pcie = arg; + struct pcie_port *pp = &artpec6_pcie->pp; return dw_handle_msi_irq(pp); } -static int artpec6_add_pcie_port(struct pcie_port *pp, +static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie, struct platform_device *pdev) { + struct pcie_port *pp = &artpec6_pcie->pp; + struct device *dev = pp->dev; int ret; if (IS_ENABLED(CONFIG_PCI_MSI)) { pp->msi_irq = platform_get_irq_byname(pdev, "msi"); if (pp->msi_irq <= 0) { - dev_err(&pdev->dev, "failed to get MSI irq\n"); + dev_err(dev, "failed to get MSI irq\n"); return -ENODEV; } - ret = devm_request_irq(&pdev->dev, pp->msi_irq, + ret = devm_request_irq(dev, pp->msi_irq, artpec6_pcie_msi_handler, IRQF_SHARED | IRQF_NO_THREAD, - "artpec6-pcie-msi", pp); + "artpec6-pcie-msi", artpec6_pcie); if (ret) { - dev_err(&pdev->dev, "failed to request MSI irq\n"); + dev_err(dev, "failed to request MSI irq\n"); return ret; } } @@ -218,7 +222,7 @@ static int artpec6_add_pcie_port(struct pcie_port *pp, ret = dw_pcie_host_init(pp); if (ret) { - dev_err(&pdev->dev, "failed to initialize host\n"); + dev_err(dev, "failed to initialize host\n"); return ret; } @@ -227,41 +231,40 @@ static int artpec6_add_pcie_port(struct pcie_port *pp, static int artpec6_pcie_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct artpec6_pcie *artpec6_pcie; struct pcie_port *pp; struct resource *dbi_base; struct resource *phy_base; int ret; - artpec6_pcie = devm_kzalloc(&pdev->dev, sizeof(*artpec6_pcie), - GFP_KERNEL); + artpec6_pcie = devm_kzalloc(dev, sizeof(*artpec6_pcie), GFP_KERNEL); if (!artpec6_pcie) return -ENOMEM; pp = &artpec6_pcie->pp; - pp->dev = &pdev->dev; + pp->dev = dev; dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base); + pp->dbi_base = devm_ioremap_resource(dev, dbi_base); if (IS_ERR(pp->dbi_base)) return PTR_ERR(pp->dbi_base); phy_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); - artpec6_pcie->phy_base = devm_ioremap_resource(&pdev->dev, phy_base); + artpec6_pcie->phy_base = devm_ioremap_resource(dev, phy_base); if (IS_ERR(artpec6_pcie->phy_base)) return PTR_ERR(artpec6_pcie->phy_base); artpec6_pcie->regmap = - syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + syscon_regmap_lookup_by_phandle(dev->of_node, "axis,syscon-pcie"); if (IS_ERR(artpec6_pcie->regmap)) return PTR_ERR(artpec6_pcie->regmap); - ret = artpec6_add_pcie_port(pp, pdev); + ret = artpec6_add_pcie_port(artpec6_pcie, pdev); if (ret < 0) return ret; - platform_set_drvdata(pdev, artpec6_pcie); return 0; } diff --git a/drivers/pci/host/pcie-designware-plat.c b/drivers/pci/host/pcie-designware-plat.c index 17da005497a5..537f58a664fa 100644 --- a/drivers/pci/host/pcie-designware-plat.c +++ b/drivers/pci/host/pcie-designware-plat.c @@ -25,8 +25,7 @@ #include "pcie-designware.h" struct dw_plat_pcie { - void __iomem *mem_base; - struct pcie_port pp; + struct pcie_port pp; /* pp.dbi_base is DT 0th resource */ }; static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg) @@ -52,6 +51,7 @@ static struct pcie_host_ops dw_plat_pcie_host_ops = { static int dw_plat_add_pcie_port(struct pcie_port *pp, struct platform_device *pdev) { + struct device *dev = pp->dev; int ret; pp->irq = platform_get_irq(pdev, 1); @@ -63,11 +63,11 @@ static int dw_plat_add_pcie_port(struct pcie_port *pp, if (pp->msi_irq < 0) return pp->msi_irq; - ret = devm_request_irq(&pdev->dev, pp->msi_irq, + ret = devm_request_irq(dev, pp->msi_irq, dw_plat_pcie_msi_irq_handler, IRQF_SHARED, "dw-plat-pcie-msi", pp); if (ret) { - dev_err(&pdev->dev, "failed to request MSI IRQ\n"); + dev_err(dev, "failed to request MSI IRQ\n"); return ret; } } @@ -77,7 +77,7 @@ static int dw_plat_add_pcie_port(struct pcie_port *pp, ret = dw_pcie_host_init(pp); if (ret) { - dev_err(&pdev->dev, "failed to initialize host\n"); + dev_err(dev, "failed to initialize host\n"); return ret; } @@ -86,31 +86,28 @@ static int dw_plat_add_pcie_port(struct pcie_port *pp, static int dw_plat_pcie_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct dw_plat_pcie *dw_plat_pcie; struct pcie_port *pp; struct resource *res; /* Resource from DT */ int ret; - dw_plat_pcie = devm_kzalloc(&pdev->dev, sizeof(*dw_plat_pcie), - GFP_KERNEL); + dw_plat_pcie = devm_kzalloc(dev, sizeof(*dw_plat_pcie), GFP_KERNEL); if (!dw_plat_pcie) return -ENOMEM; pp = &dw_plat_pcie->pp; - pp->dev = &pdev->dev; + pp->dev = dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dw_plat_pcie->mem_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dw_plat_pcie->mem_base)) - return PTR_ERR(dw_plat_pcie->mem_base); - - pp->dbi_base = dw_plat_pcie->mem_base; + pp->dbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pp->dbi_base)) + return PTR_ERR(pp->dbi_base); ret = dw_plat_add_pcie_port(pp, pdev); if (ret < 0) return ret; - platform_set_drvdata(pdev, dw_plat_pcie); return 0; } diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 74da71ea544a..035f50c03281 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -141,41 +141,35 @@ int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val) return PCIBIOS_SUCCESSFUL; } -static inline u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg) +u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg) { if (pp->ops->readl_rc) - return pp->ops->readl_rc(pp, pp->dbi_base + reg); + return pp->ops->readl_rc(pp, reg); return readl(pp->dbi_base + reg); } -static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg) +void dw_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val) { if (pp->ops->writel_rc) - pp->ops->writel_rc(pp, val, pp->dbi_base + reg); + pp->ops->writel_rc(pp, reg, val); else writel(val, pp->dbi_base + reg); } -static inline u32 dw_pcie_readl_unroll(struct pcie_port *pp, u32 index, u32 reg) +static u32 dw_pcie_readl_unroll(struct pcie_port *pp, u32 index, u32 reg) { u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); - if (pp->ops->readl_rc) - return pp->ops->readl_rc(pp, pp->dbi_base + offset + reg); - - return readl(pp->dbi_base + offset + reg); + return dw_pcie_readl_rc(pp, offset + reg); } -static inline void dw_pcie_writel_unroll(struct pcie_port *pp, u32 index, - u32 val, u32 reg) +static void dw_pcie_writel_unroll(struct pcie_port *pp, u32 index, u32 reg, + u32 val) { u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); - if (pp->ops->writel_rc) - pp->ops->writel_rc(pp, val, pp->dbi_base + offset + reg); - else - writel(val, pp->dbi_base + offset + reg); + dw_pcie_writel_rc(pp, offset + reg, val); } static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, @@ -202,35 +196,35 @@ static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index, u32 retries, val; if (pp->iatu_unroll_enabled) { - dw_pcie_writel_unroll(pp, index, - lower_32_bits(cpu_addr), PCIE_ATU_UNR_LOWER_BASE); - dw_pcie_writel_unroll(pp, index, - upper_32_bits(cpu_addr), PCIE_ATU_UNR_UPPER_BASE); - dw_pcie_writel_unroll(pp, index, - lower_32_bits(cpu_addr + size - 1), PCIE_ATU_UNR_LIMIT); - dw_pcie_writel_unroll(pp, index, - lower_32_bits(pci_addr), PCIE_ATU_UNR_LOWER_TARGET); - dw_pcie_writel_unroll(pp, index, - upper_32_bits(pci_addr), PCIE_ATU_UNR_UPPER_TARGET); - dw_pcie_writel_unroll(pp, index, - type, PCIE_ATU_UNR_REGION_CTRL1); - dw_pcie_writel_unroll(pp, index, - PCIE_ATU_ENABLE, PCIE_ATU_UNR_REGION_CTRL2); + dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LOWER_BASE, + lower_32_bits(cpu_addr)); + dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_UPPER_BASE, + upper_32_bits(cpu_addr)); + dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LIMIT, + lower_32_bits(cpu_addr + size - 1)); + dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LOWER_TARGET, + lower_32_bits(pci_addr)); + dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_UPPER_TARGET, + upper_32_bits(pci_addr)); + dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_REGION_CTRL1, + type); + dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_REGION_CTRL2, + PCIE_ATU_ENABLE); } else { - dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index, - PCIE_ATU_VIEWPORT); - dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr), - PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr), - PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1), - PCIE_ATU_LIMIT); - dw_pcie_writel_rc(pp, lower_32_bits(pci_addr), - PCIE_ATU_LOWER_TARGET); - dw_pcie_writel_rc(pp, upper_32_bits(pci_addr), - PCIE_ATU_UPPER_TARGET); - dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); + dw_pcie_writel_rc(pp, PCIE_ATU_VIEWPORT, + PCIE_ATU_REGION_OUTBOUND | index); + dw_pcie_writel_rc(pp, PCIE_ATU_LOWER_BASE, + lower_32_bits(cpu_addr)); + dw_pcie_writel_rc(pp, PCIE_ATU_UPPER_BASE, + upper_32_bits(cpu_addr)); + dw_pcie_writel_rc(pp, PCIE_ATU_LIMIT, + lower_32_bits(cpu_addr + size - 1)); + dw_pcie_writel_rc(pp, PCIE_ATU_LOWER_TARGET, + lower_32_bits(pci_addr)); + dw_pcie_writel_rc(pp, PCIE_ATU_UPPER_TARGET, + upper_32_bits(pci_addr)); + dw_pcie_writel_rc(pp, PCIE_ATU_CR1, type); + dw_pcie_writel_rc(pp, PCIE_ATU_CR2, PCIE_ATU_ENABLE); } /* @@ -760,8 +754,8 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, return ret; } -static int dw_pcie_valid_config(struct pcie_port *pp, - struct pci_bus *bus, int dev) +static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus, + int dev) { /* If there is no link, then there is no device */ if (bus->number != pp->root_bus_nr) { @@ -781,7 +775,7 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, { struct pcie_port *pp = bus->sysdata; - if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) { + if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn))) { *val = 0xffffffff; return PCIBIOS_DEVICE_NOT_FOUND; } @@ -797,7 +791,7 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, { struct pcie_port *pp = bus->sysdata; - if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) + if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn))) return PCIBIOS_DEVICE_NOT_FOUND; if (bus->number == pp->root_bus_nr) @@ -835,7 +829,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp) dev_err(pp->dev, "num-lanes %u: invalid value\n", pp->lanes); return; } - dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL); + dw_pcie_writel_rc(pp, PCIE_PORT_LINK_CONTROL, val); /* set link width speed control register */ val = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL); @@ -854,30 +848,30 @@ void dw_pcie_setup_rc(struct pcie_port *pp) val |= PORT_LOGIC_LINK_WIDTH_8_LANES; break; } - dw_pcie_writel_rc(pp, val, PCIE_LINK_WIDTH_SPEED_CONTROL); + dw_pcie_writel_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, val); /* setup RC BARs */ - dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_0); - dw_pcie_writel_rc(pp, 0x00000000, PCI_BASE_ADDRESS_1); + dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 0x00000004); + dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_1, 0x00000000); /* setup interrupt pins */ val = dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE); val &= 0xffff00ff; val |= 0x00000100; - dw_pcie_writel_rc(pp, val, PCI_INTERRUPT_LINE); + dw_pcie_writel_rc(pp, PCI_INTERRUPT_LINE, val); /* setup bus numbers */ val = dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS); val &= 0xff000000; val |= 0x00010100; - dw_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS); + dw_pcie_writel_rc(pp, PCI_PRIMARY_BUS, val); /* setup command register */ val = dw_pcie_readl_rc(pp, PCI_COMMAND); val &= 0xffff0000; val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_SERR; - dw_pcie_writel_rc(pp, val, PCI_COMMAND); + dw_pcie_writel_rc(pp, PCI_COMMAND, val); /* * If the platform provides ->rd_other_conf, it means the platform diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index c8e5bc647f49..a567ea288ee2 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -54,9 +54,8 @@ struct pcie_port { }; struct pcie_host_ops { - u32 (*readl_rc)(struct pcie_port *pp, void __iomem *dbi_base); - void (*writel_rc)(struct pcie_port *pp, - u32 val, void __iomem *dbi_base); + u32 (*readl_rc)(struct pcie_port *pp, u32 reg); + void (*writel_rc)(struct pcie_port *pp, u32 reg, u32 val); int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val); int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val); int (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus, @@ -73,6 +72,8 @@ struct pcie_host_ops { int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip); }; +u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg); +void dw_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val); int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val); int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val); irqreturn_t dw_handle_msi_irq(struct pcie_port *pp); diff --git a/drivers/pci/host/pcie-hisi.c b/drivers/pci/host/pcie-hisi.c index 7ee9dfcc45fb..56154c25980c 100644 --- a/drivers/pci/host/pcie-hisi.c +++ b/drivers/pci/host/pcie-hisi.c @@ -22,51 +22,38 @@ #include "pcie-designware.h" -#define PCIE_LTSSM_LINKUP_STATE 0x11 -#define PCIE_LTSSM_STATE_MASK 0x3F -#define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818 -#define PCIE_SYS_STATE4 0x31c -#define PCIE_HIP06_CTRL_OFF 0x1000 +#define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818 +#define PCIE_HIP06_CTRL_OFF 0x1000 +#define PCIE_SYS_STATE4 (PCIE_HIP06_CTRL_OFF + 0x31c) +#define PCIE_LTSSM_LINKUP_STATE 0x11 +#define PCIE_LTSSM_STATE_MASK 0x3F #define to_hisi_pcie(x) container_of(x, struct hisi_pcie, pp) struct hisi_pcie; struct pcie_soc_ops { - int (*hisi_pcie_link_up)(struct hisi_pcie *pcie); + int (*hisi_pcie_link_up)(struct hisi_pcie *hisi_pcie); }; struct hisi_pcie { + struct pcie_port pp; /* pp.dbi_base is DT rc_dbi */ struct regmap *subctrl; - void __iomem *reg_base; u32 port_id; - struct pcie_port pp; struct pcie_soc_ops *soc_ops; }; -static inline void hisi_pcie_apb_writel(struct hisi_pcie *pcie, - u32 val, u32 reg) -{ - writel(val, pcie->reg_base + reg); -} - -static inline u32 hisi_pcie_apb_readl(struct hisi_pcie *pcie, u32 reg) -{ - return readl(pcie->reg_base + reg); -} - /* HipXX PCIe host only supports 32-bit config access */ static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size, u32 *val) { u32 reg; u32 reg_val; - struct hisi_pcie *pcie = to_hisi_pcie(pp); void *walker = ®_val; walker += (where & 0x3); reg = where & ~0x3; - reg_val = hisi_pcie_apb_readl(pcie, reg); + reg_val = dw_pcie_readl_rc(pp, reg); if (size == 1) *val = *(u8 __force *) walker; @@ -86,21 +73,20 @@ static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size, { u32 reg_val; u32 reg; - struct hisi_pcie *pcie = to_hisi_pcie(pp); void *walker = ®_val; walker += (where & 0x3); reg = where & ~0x3; if (size == 4) - hisi_pcie_apb_writel(pcie, val, reg); + dw_pcie_writel_rc(pp, reg, val); else if (size == 2) { - reg_val = hisi_pcie_apb_readl(pcie, reg); + reg_val = dw_pcie_readl_rc(pp, reg); *(u16 __force *) walker = val; - hisi_pcie_apb_writel(pcie, reg_val, reg); + dw_pcie_writel_rc(pp, reg, reg_val); } else if (size == 1) { - reg_val = hisi_pcie_apb_readl(pcie, reg); + reg_val = dw_pcie_readl_rc(pp, reg); *(u8 __force *) walker = val; - hisi_pcie_apb_writel(pcie, reg_val, reg); + dw_pcie_writel_rc(pp, reg, reg_val); } else return PCIBIOS_BAD_REGISTER_NUMBER; @@ -119,10 +105,10 @@ static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie) static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie) { + struct pcie_port *pp = &hisi_pcie->pp; u32 val; - val = hisi_pcie_apb_readl(hisi_pcie, PCIE_HIP06_CTRL_OFF + - PCIE_SYS_STATE4); + val = dw_pcie_readl_rc(pp, PCIE_SYS_STATE4); return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); } @@ -140,19 +126,20 @@ static struct pcie_host_ops hisi_pcie_host_ops = { .link_up = hisi_pcie_link_up, }; -static int hisi_add_pcie_port(struct pcie_port *pp, - struct platform_device *pdev) +static int hisi_add_pcie_port(struct hisi_pcie *hisi_pcie, + struct platform_device *pdev) { + struct pcie_port *pp = &hisi_pcie->pp; + struct device *dev = pp->dev; int ret; u32 port_id; - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); - if (of_property_read_u32(pdev->dev.of_node, "port-id", &port_id)) { - dev_err(&pdev->dev, "failed to read port-id\n"); + if (of_property_read_u32(dev->of_node, "port-id", &port_id)) { + dev_err(dev, "failed to read port-id\n"); return -EINVAL; } if (port_id > 3) { - dev_err(&pdev->dev, "Invalid port-id: %d\n", port_id); + dev_err(dev, "Invalid port-id: %d\n", port_id); return -EINVAL; } hisi_pcie->port_id = port_id; @@ -161,7 +148,7 @@ static int hisi_add_pcie_port(struct pcie_port *pp, ret = dw_pcie_host_init(pp); if (ret) { - dev_err(&pdev->dev, "failed to initialize host\n"); + dev_err(dev, "failed to initialize host\n"); return ret; } @@ -170,6 +157,7 @@ static int hisi_add_pcie_port(struct pcie_port *pp, static int hisi_pcie_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct hisi_pcie *hisi_pcie; struct pcie_port *pp; const struct of_device_id *match; @@ -177,40 +165,36 @@ static int hisi_pcie_probe(struct platform_device *pdev) struct device_driver *driver; int ret; - hisi_pcie = devm_kzalloc(&pdev->dev, sizeof(*hisi_pcie), GFP_KERNEL); + hisi_pcie = devm_kzalloc(dev, sizeof(*hisi_pcie), GFP_KERNEL); if (!hisi_pcie) return -ENOMEM; pp = &hisi_pcie->pp; - pp->dev = &pdev->dev; - driver = (pdev->dev).driver; + pp->dev = dev; + driver = dev->driver; - match = of_match_device(driver->of_match_table, &pdev->dev); + match = of_match_device(driver->of_match_table, dev); hisi_pcie->soc_ops = (struct pcie_soc_ops *) match->data; hisi_pcie->subctrl = syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl"); if (IS_ERR(hisi_pcie->subctrl)) { - dev_err(pp->dev, "cannot get subctrl base\n"); + dev_err(dev, "cannot get subctrl base\n"); return PTR_ERR(hisi_pcie->subctrl); } reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi"); - hisi_pcie->reg_base = devm_ioremap_resource(&pdev->dev, reg); - if (IS_ERR(hisi_pcie->reg_base)) { - dev_err(pp->dev, "cannot get rc_dbi base\n"); - return PTR_ERR(hisi_pcie->reg_base); + pp->dbi_base = devm_ioremap_resource(dev, reg); + if (IS_ERR(pp->dbi_base)) { + dev_err(dev, "cannot get rc_dbi base\n"); + return PTR_ERR(pp->dbi_base); } - hisi_pcie->pp.dbi_base = hisi_pcie->reg_base; - - ret = hisi_add_pcie_port(pp, pdev); + ret = hisi_add_pcie_port(hisi_pcie, pdev); if (ret) return ret; - platform_set_drvdata(pdev, hisi_pcie); - - dev_warn(pp->dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n"); + dev_warn(dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n"); return 0; } diff --git a/drivers/pci/host/pcie-iproc-bcma.c b/drivers/pci/host/pcie-iproc-bcma.c index 0d7bee4a0d26..8ce089043a27 100644 --- a/drivers/pci/host/pcie-iproc-bcma.c +++ b/drivers/pci/host/pcie-iproc-bcma.c @@ -42,19 +42,24 @@ static int iproc_pcie_bcma_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) static int iproc_pcie_bcma_probe(struct bcma_device *bdev) { + struct device *dev = &bdev->dev; struct iproc_pcie *pcie; LIST_HEAD(res); struct resource res_mem; int ret; - pcie = devm_kzalloc(&bdev->dev, sizeof(*pcie), GFP_KERNEL); + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) return -ENOMEM; - pcie->dev = &bdev->dev; - bcma_set_drvdata(bdev, pcie); + pcie->dev = dev; pcie->base = bdev->io_addr; + if (!pcie->base) { + dev_err(dev, "no controller registers\n"); + return -ENOMEM; + } + pcie->base_addr = bdev->addr; res_mem.start = bdev->addr_s[0]; @@ -67,10 +72,11 @@ static int iproc_pcie_bcma_probe(struct bcma_device *bdev) ret = iproc_pcie_setup(pcie, &res); if (ret) - dev_err(pcie->dev, "PCIe controller setup failed\n"); + dev_err(dev, "PCIe controller setup failed\n"); pci_free_resource_list(&res); + bcma_set_drvdata(bdev, pcie); return ret; } diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c index 1738c5288eb6..a3de087976b3 100644 --- a/drivers/pci/host/pcie-iproc-platform.c +++ b/drivers/pci/host/pcie-iproc-platform.c @@ -40,35 +40,35 @@ MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table); static int iproc_pcie_pltfm_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; const struct of_device_id *of_id; struct iproc_pcie *pcie; - struct device_node *np = pdev->dev.of_node; + struct device_node *np = dev->of_node; struct resource reg; resource_size_t iobase = 0; LIST_HEAD(res); int ret; - of_id = of_match_device(iproc_pcie_of_match_table, &pdev->dev); + of_id = of_match_device(iproc_pcie_of_match_table, dev); if (!of_id) return -EINVAL; - pcie = devm_kzalloc(&pdev->dev, sizeof(struct iproc_pcie), GFP_KERNEL); + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) return -ENOMEM; - pcie->dev = &pdev->dev; + pcie->dev = dev; pcie->type = (enum iproc_pcie_type)of_id->data; - platform_set_drvdata(pdev, pcie); ret = of_address_to_resource(np, 0, ®); if (ret < 0) { - dev_err(pcie->dev, "unable to obtain controller resources\n"); + dev_err(dev, "unable to obtain controller resources\n"); return ret; } - pcie->base = devm_ioremap(pcie->dev, reg.start, resource_size(®)); + pcie->base = devm_ioremap(dev, reg.start, resource_size(®)); if (!pcie->base) { - dev_err(pcie->dev, "unable to map controller registers\n"); + dev_err(dev, "unable to map controller registers\n"); return -ENOMEM; } pcie->base_addr = reg.start; @@ -79,7 +79,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) ret = of_property_read_u32(np, "brcm,pcie-ob-axi-offset", &val); if (ret) { - dev_err(pcie->dev, + dev_err(dev, "missing brcm,pcie-ob-axi-offset property\n"); return ret; } @@ -88,7 +88,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) ret = of_property_read_u32(np, "brcm,pcie-ob-window-size", &val); if (ret) { - dev_err(pcie->dev, + dev_err(dev, "missing brcm,pcie-ob-window-size property\n"); return ret; } @@ -101,7 +101,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) } /* PHY use is optional */ - pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy"); + pcie->phy = devm_phy_get(dev, "pcie-phy"); if (IS_ERR(pcie->phy)) { if (PTR_ERR(pcie->phy) == -EPROBE_DEFER) return -EPROBE_DEFER; @@ -110,7 +110,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &iobase); if (ret) { - dev_err(pcie->dev, + dev_err(dev, "unable to get PCI host bridge resources\n"); return ret; } @@ -119,10 +119,11 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) ret = iproc_pcie_setup(pcie, &res); if (ret) - dev_err(pcie->dev, "PCIe controller setup failed\n"); + dev_err(dev, "PCIe controller setup failed\n"); pci_free_resource_list(&res); + platform_set_drvdata(pdev, pcie); return ret; } diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c index e167b2f0098d..0b999a9fb843 100644 --- a/drivers/pci/host/pcie-iproc.c +++ b/drivers/pci/host/pcie-iproc.c @@ -63,6 +63,8 @@ #define OARR_SIZE_CFG_SHIFT 1 #define OARR_SIZE_CFG BIT(OARR_SIZE_CFG_SHIFT) +#define PCI_EXP_CAP 0xac + #define MAX_NUM_OB_WINDOWS 2 #define IPROC_PCIE_REG_INVALID 0xffff @@ -258,9 +260,10 @@ static void iproc_pcie_reset(struct iproc_pcie *pcie) static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus) { + struct device *dev = pcie->dev; u8 hdr_type; u32 link_ctrl, class, val; - u16 pos, link_status; + u16 pos = PCI_EXP_CAP, link_status; bool link_is_active = false; /* @@ -272,14 +275,14 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus) val = iproc_pcie_read_reg(pcie, IPROC_PCIE_LINK_STATUS); if (!(val & PCIE_PHYLINKUP) || !(val & PCIE_DL_ACTIVE)) { - dev_err(pcie->dev, "PHY or data link is INACTIVE!\n"); + dev_err(dev, "PHY or data link is INACTIVE!\n"); return -ENODEV; } /* make sure we are not in EP mode */ pci_bus_read_config_byte(bus, 0, PCI_HEADER_TYPE, &hdr_type); if ((hdr_type & 0x7f) != PCI_HEADER_TYPE_BRIDGE) { - dev_err(pcie->dev, "in EP mode, hdr=%#02x\n", hdr_type); + dev_err(dev, "in EP mode, hdr=%#02x\n", hdr_type); return -EFAULT; } @@ -293,30 +296,27 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus) pci_bus_write_config_dword(bus, 0, PCI_BRIDGE_CTRL_REG_OFFSET, class); /* check link status to see if link is active */ - pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP); pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA, &link_status); if (link_status & PCI_EXP_LNKSTA_NLW) link_is_active = true; if (!link_is_active) { /* try GEN 1 link speed */ -#define PCI_LINK_STATUS_CTRL_2_OFFSET 0x0dc #define PCI_TARGET_LINK_SPEED_MASK 0xf #define PCI_TARGET_LINK_SPEED_GEN2 0x2 #define PCI_TARGET_LINK_SPEED_GEN1 0x1 pci_bus_read_config_dword(bus, 0, - PCI_LINK_STATUS_CTRL_2_OFFSET, + pos + PCI_EXP_LNKCTL2, &link_ctrl); if ((link_ctrl & PCI_TARGET_LINK_SPEED_MASK) == PCI_TARGET_LINK_SPEED_GEN2) { link_ctrl &= ~PCI_TARGET_LINK_SPEED_MASK; link_ctrl |= PCI_TARGET_LINK_SPEED_GEN1; pci_bus_write_config_dword(bus, 0, - PCI_LINK_STATUS_CTRL_2_OFFSET, + pos + PCI_EXP_LNKCTL2, link_ctrl); msleep(100); - pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP); pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA, &link_status); if (link_status & PCI_EXP_LNKSTA_NLW) @@ -324,7 +324,7 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus) } } - dev_info(pcie->dev, "link: %s\n", link_is_active ? "UP" : "DOWN"); + dev_info(dev, "link: %s\n", link_is_active ? "UP" : "DOWN"); return link_is_active ? 0 : -ENODEV; } @@ -349,12 +349,13 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr, u64 pci_addr, resource_size_t size) { struct iproc_pcie_ob *ob = &pcie->ob; + struct device *dev = pcie->dev; unsigned i; u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS; u64 remainder; if (size > max_size) { - dev_err(pcie->dev, + dev_err(dev, "res size %pap exceeds max supported size 0x%llx\n", &size, max_size); return -EINVAL; @@ -362,15 +363,14 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr, div64_u64_rem(size, ob->window_size, &remainder); if (remainder) { - dev_err(pcie->dev, + dev_err(dev, "res size %pap needs to be multiple of window size %pap\n", &size, &ob->window_size); return -EINVAL; } if (axi_addr < ob->axi_offset) { - dev_err(pcie->dev, - "axi address %pap less than offset %pap\n", + dev_err(dev, "axi address %pap less than offset %pap\n", &axi_addr, &ob->axi_offset); return -EINVAL; } @@ -406,6 +406,7 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr, static int iproc_pcie_map_ranges(struct iproc_pcie *pcie, struct list_head *resources) { + struct device *dev = pcie->dev; struct resource_entry *window; int ret; @@ -425,7 +426,7 @@ static int iproc_pcie_map_ranges(struct iproc_pcie *pcie, return ret; break; default: - dev_err(pcie->dev, "invalid resource %pR\n", res); + dev_err(dev, "invalid resource %pR\n", res); return -EINVAL; } } @@ -455,26 +456,25 @@ static void iproc_pcie_msi_disable(struct iproc_pcie *pcie) int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) { + struct device *dev; int ret; void *sysdata; struct pci_bus *bus; - if (!pcie || !pcie->dev || !pcie->base) - return -EINVAL; - - ret = devm_request_pci_bus_resources(pcie->dev, res); + dev = pcie->dev; + ret = devm_request_pci_bus_resources(dev, res); if (ret) return ret; ret = phy_init(pcie->phy); if (ret) { - dev_err(pcie->dev, "unable to initialize PCIe PHY\n"); + dev_err(dev, "unable to initialize PCIe PHY\n"); return ret; } ret = phy_power_on(pcie->phy); if (ret) { - dev_err(pcie->dev, "unable to power on PCIe PHY\n"); + dev_err(dev, "unable to power on PCIe PHY\n"); goto err_exit_phy; } @@ -486,7 +486,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) pcie->reg_offsets = iproc_pcie_reg_paxc; break; default: - dev_err(pcie->dev, "incompatible iProc PCIe interface\n"); + dev_err(dev, "incompatible iProc PCIe interface\n"); ret = -EINVAL; goto err_power_off_phy; } @@ -496,7 +496,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) if (pcie->need_ob_cfg) { ret = iproc_pcie_map_ranges(pcie, res); if (ret) { - dev_err(pcie->dev, "map failed\n"); + dev_err(dev, "map failed\n"); goto err_power_off_phy; } } @@ -508,9 +508,9 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) sysdata = pcie; #endif - bus = pci_create_root_bus(pcie->dev, 0, &iproc_pcie_ops, sysdata, res); + bus = pci_create_root_bus(dev, 0, &iproc_pcie_ops, sysdata, res); if (!bus) { - dev_err(pcie->dev, "unable to create PCI root bus\n"); + dev_err(dev, "unable to create PCI root bus\n"); ret = -ENOMEM; goto err_power_off_phy; } @@ -518,7 +518,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) ret = iproc_pcie_check_link(pcie, bus); if (ret) { - dev_err(pcie->dev, "no PCIe EP device detected\n"); + dev_err(dev, "no PCIe EP device detected\n"); goto err_rm_root_bus; } @@ -526,7 +526,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) if (IS_ENABLED(CONFIG_PCI_MSI)) if (iproc_pcie_msi_enable(pcie)) - dev_info(pcie->dev, "not using iProc MSI\n"); + dev_info(dev, "not using iProc MSI\n"); pci_scan_child_bus(bus); pci_assign_unassigned_bus_resources(bus); diff --git a/drivers/pci/host/pcie-qcom.c b/drivers/pci/host/pcie-qcom.c index 5ec2d440a6b7..ef0a84c7a588 100644 --- a/drivers/pci/host/pcie-qcom.c +++ b/drivers/pci/host/pcie-qcom.c @@ -86,12 +86,10 @@ struct qcom_pcie_ops { }; struct qcom_pcie { - struct pcie_port pp; - struct device *dev; + struct pcie_port pp; /* pp.dbi_base is DT dbi */ + void __iomem *parf; /* DT parf */ + void __iomem *elbi; /* DT elbi */ union qcom_pcie_resources res; - void __iomem *parf; - void __iomem *dbi; - void __iomem *elbi; struct phy *phy; struct gpio_desc *reset; struct qcom_pcie_ops *ops; @@ -136,7 +134,7 @@ static int qcom_pcie_establish_link(struct qcom_pcie *pcie) static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_v0 *res = &pcie->res.v0; - struct device *dev = pcie->dev; + struct device *dev = pcie->pp.dev; res->vdda = devm_regulator_get(dev, "vdda"); if (IS_ERR(res->vdda)) @@ -188,7 +186,7 @@ static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie) static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie) { struct qcom_pcie_resources_v1 *res = &pcie->res.v1; - struct device *dev = pcie->dev; + struct device *dev = pcie->pp.dev; res->vdda = devm_regulator_get(dev, "vdda"); if (IS_ERR(res->vdda)) @@ -237,7 +235,7 @@ static void qcom_pcie_deinit_v0(struct qcom_pcie *pcie) static int qcom_pcie_init_v0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_v0 *res = &pcie->res.v0; - struct device *dev = pcie->dev; + struct device *dev = pcie->pp.dev; u32 val; int ret; @@ -359,7 +357,7 @@ static void qcom_pcie_deinit_v1(struct qcom_pcie *pcie) static int qcom_pcie_init_v1(struct qcom_pcie *pcie) { struct qcom_pcie_resources_v1 *res = &pcie->res.v1; - struct device *dev = pcie->dev; + struct device *dev = pcie->pp.dev; int ret; ret = reset_control_deassert(res->core); @@ -426,7 +424,7 @@ err_res: static int qcom_pcie_link_up(struct pcie_port *pp) { struct qcom_pcie *pcie = to_qcom_pcie(pp); - u16 val = readw(pcie->dbi + PCIE20_CAP + PCI_EXP_LNKSTA); + u16 val = readw(pcie->pp.dbi_base + PCIE20_CAP + PCI_EXP_LNKSTA); return !!(val & PCI_EXP_LNKSTA_DLLLA); } @@ -509,8 +507,8 @@ static int qcom_pcie_probe(struct platform_device *pdev) if (!pcie) return -ENOMEM; + pp = &pcie->pp; pcie->ops = (struct qcom_pcie_ops *)of_device_get_match_data(dev); - pcie->dev = dev; pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW); if (IS_ERR(pcie->reset)) @@ -522,9 +520,9 @@ static int qcom_pcie_probe(struct platform_device *pdev) return PTR_ERR(pcie->parf); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); - pcie->dbi = devm_ioremap_resource(dev, res); - if (IS_ERR(pcie->dbi)) - return PTR_ERR(pcie->dbi); + pp->dbi_base = devm_ioremap_resource(dev, res); + if (IS_ERR(pp->dbi_base)) + return PTR_ERR(pp->dbi_base); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); pcie->elbi = devm_ioremap_resource(dev, res); @@ -539,9 +537,7 @@ static int qcom_pcie_probe(struct platform_device *pdev) if (ret) return ret; - pp = &pcie->pp; pp->dev = dev; - pp->dbi_base = pcie->dbi; pp->root_bus_nr = -1; pp->ops = &qcom_pcie_dw_ops; @@ -569,8 +565,6 @@ static int qcom_pcie_probe(struct platform_device *pdev) return ret; } - platform_set_drvdata(pdev, pcie); - return 0; } diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index e06b1d3b4dea..62700d1896f4 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -31,8 +31,6 @@ #include <linux/pm_runtime.h> #include <linux/slab.h> -#define DRV_NAME "rcar-pcie" - #define PCIECAR 0x000010 #define PCIECCTLR 0x000018 #define CONFIG_SEND_ENABLE (1 << 31) @@ -397,6 +395,7 @@ static int rcar_pcie_setup(struct list_head *resource, struct rcar_pcie *pci) static void rcar_pcie_force_speedup(struct rcar_pcie *pcie) { + struct device *dev = pcie->dev; unsigned int timeout = 1000; u32 macsr; @@ -404,7 +403,7 @@ static void rcar_pcie_force_speedup(struct rcar_pcie *pcie) return; if (rcar_pci_read_reg(pcie, MACCTLR) & SPEED_CHANGE) { - dev_err(pcie->dev, "Speed change already in progress\n"); + dev_err(dev, "Speed change already in progress\n"); return; } @@ -433,7 +432,7 @@ static void rcar_pcie_force_speedup(struct rcar_pcie *pcie) rcar_pci_write_reg(pcie, macsr, MACSR); if (macsr & SPCHGFAIL) - dev_err(pcie->dev, "Speed change failed\n"); + dev_err(dev, "Speed change failed\n"); goto done; } @@ -441,15 +440,16 @@ static void rcar_pcie_force_speedup(struct rcar_pcie *pcie) msleep(1); }; - dev_err(pcie->dev, "Speed change timed out\n"); + dev_err(dev, "Speed change timed out\n"); done: - dev_info(pcie->dev, "Current link speed is %s GT/s\n", + dev_info(dev, "Current link speed is %s GT/s\n", (macsr & LINK_SPEED) == LINK_SPEED_5_0GTS ? "5" : "2.5"); } static int rcar_pcie_enable(struct rcar_pcie *pcie) { + struct device *dev = pcie->dev; struct pci_bus *bus, *child; LIST_HEAD(res); @@ -461,14 +461,14 @@ static int rcar_pcie_enable(struct rcar_pcie *pcie) pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS); if (IS_ENABLED(CONFIG_PCI_MSI)) - bus = pci_scan_root_bus_msi(pcie->dev, pcie->root_bus_nr, + bus = pci_scan_root_bus_msi(dev, pcie->root_bus_nr, &rcar_pcie_ops, pcie, &res, &pcie->msi.chip); else - bus = pci_scan_root_bus(pcie->dev, pcie->root_bus_nr, + bus = pci_scan_root_bus(dev, pcie->root_bus_nr, &rcar_pcie_ops, pcie, &res); if (!bus) { - dev_err(pcie->dev, "Scanning rootbus failed"); + dev_err(dev, "Scanning rootbus failed"); return -ENODEV; } @@ -487,6 +487,7 @@ static int rcar_pcie_enable(struct rcar_pcie *pcie) static int phy_wait_for_ack(struct rcar_pcie *pcie) { + struct device *dev = pcie->dev; unsigned int timeout = 100; while (timeout--) { @@ -496,7 +497,7 @@ static int phy_wait_for_ack(struct rcar_pcie *pcie) udelay(100); } - dev_err(pcie->dev, "Access to PCIe phy timed out\n"); + dev_err(dev, "Access to PCIe phy timed out\n"); return -ETIMEDOUT; } @@ -697,6 +698,7 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data) { struct rcar_pcie *pcie = data; struct rcar_msi *msi = &pcie->msi; + struct device *dev = pcie->dev; unsigned long reg; reg = rcar_pci_read_reg(pcie, PCIEMSIFR); @@ -717,10 +719,10 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data) if (test_bit(index, msi->used)) generic_handle_irq(irq); else - dev_info(pcie->dev, "unhandled MSI\n"); + dev_info(dev, "unhandled MSI\n"); } else { /* Unknown MSI, just clear it */ - dev_dbg(pcie->dev, "unexpected MSI\n"); + dev_dbg(dev, "unexpected MSI\n"); } /* see if there's any more pending in this vector */ @@ -843,22 +845,22 @@ static const struct irq_domain_ops msi_domain_ops = { static int rcar_pcie_enable_msi(struct rcar_pcie *pcie) { - struct platform_device *pdev = to_platform_device(pcie->dev); + struct device *dev = pcie->dev; struct rcar_msi *msi = &pcie->msi; unsigned long base; int err, i; mutex_init(&msi->lock); - msi->chip.dev = pcie->dev; + msi->chip.dev = dev; msi->chip.setup_irq = rcar_msi_setup_irq; msi->chip.setup_irqs = rcar_msi_setup_irqs; msi->chip.teardown_irq = rcar_msi_teardown_irq; - msi->domain = irq_domain_add_linear(pcie->dev->of_node, INT_PCI_MSI_NR, + msi->domain = irq_domain_add_linear(dev->of_node, INT_PCI_MSI_NR, &msi_domain_ops, &msi->chip); if (!msi->domain) { - dev_err(&pdev->dev, "failed to create IRQ domain\n"); + dev_err(dev, "failed to create IRQ domain\n"); return -ENOMEM; } @@ -866,19 +868,19 @@ static int rcar_pcie_enable_msi(struct rcar_pcie *pcie) irq_create_mapping(msi->domain, i); /* Two irqs are for MSI, but they are also used for non-MSI irqs */ - err = devm_request_irq(&pdev->dev, msi->irq1, rcar_pcie_msi_irq, + err = devm_request_irq(dev, msi->irq1, rcar_pcie_msi_irq, IRQF_SHARED | IRQF_NO_THREAD, rcar_msi_irq_chip.name, pcie); if (err < 0) { - dev_err(&pdev->dev, "failed to request IRQ: %d\n", err); + dev_err(dev, "failed to request IRQ: %d\n", err); goto err; } - err = devm_request_irq(&pdev->dev, msi->irq2, rcar_pcie_msi_irq, + err = devm_request_irq(dev, msi->irq2, rcar_pcie_msi_irq, IRQF_SHARED | IRQF_NO_THREAD, rcar_msi_irq_chip.name, pcie); if (err < 0) { - dev_err(&pdev->dev, "failed to request IRQ: %d\n", err); + dev_err(dev, "failed to request IRQ: %d\n", err); goto err; } @@ -899,32 +901,32 @@ err: return err; } -static int rcar_pcie_get_resources(struct platform_device *pdev, - struct rcar_pcie *pcie) +static int rcar_pcie_get_resources(struct rcar_pcie *pcie) { + struct device *dev = pcie->dev; struct resource res; int err, i; - err = of_address_to_resource(pdev->dev.of_node, 0, &res); + err = of_address_to_resource(dev->of_node, 0, &res); if (err) return err; - pcie->base = devm_ioremap_resource(&pdev->dev, &res); + pcie->base = devm_ioremap_resource(dev, &res); if (IS_ERR(pcie->base)) return PTR_ERR(pcie->base); - pcie->clk = devm_clk_get(&pdev->dev, "pcie"); + pcie->clk = devm_clk_get(dev, "pcie"); if (IS_ERR(pcie->clk)) { - dev_err(pcie->dev, "cannot get platform clock\n"); + dev_err(dev, "cannot get platform clock\n"); return PTR_ERR(pcie->clk); } err = clk_prepare_enable(pcie->clk); if (err) return err; - pcie->bus_clk = devm_clk_get(&pdev->dev, "pcie_bus"); + pcie->bus_clk = devm_clk_get(dev, "pcie_bus"); if (IS_ERR(pcie->bus_clk)) { - dev_err(pcie->dev, "cannot get pcie bus clock\n"); + dev_err(dev, "cannot get pcie bus clock\n"); err = PTR_ERR(pcie->bus_clk); goto fail_clk; } @@ -932,17 +934,17 @@ static int rcar_pcie_get_resources(struct platform_device *pdev, if (err) goto fail_clk; - i = irq_of_parse_and_map(pdev->dev.of_node, 0); + i = irq_of_parse_and_map(dev->of_node, 0); if (!i) { - dev_err(pcie->dev, "cannot get platform resources for msi interrupt\n"); + dev_err(dev, "cannot get platform resources for msi interrupt\n"); err = -ENOENT; goto err_map_reg; } pcie->msi.irq1 = i; - i = irq_of_parse_and_map(pdev->dev.of_node, 1); + i = irq_of_parse_and_map(dev->of_node, 1); if (!i) { - dev_err(pcie->dev, "cannot get platform resources for msi interrupt\n"); + dev_err(dev, "cannot get platform resources for msi interrupt\n"); err = -ENOENT; goto err_map_reg; } @@ -1119,60 +1121,60 @@ out_release_res: static int rcar_pcie_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct rcar_pcie *pcie; unsigned int data; const struct of_device_id *of_id; int err; int (*hw_init_fn)(struct rcar_pcie *); - pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) return -ENOMEM; - pcie->dev = &pdev->dev; - platform_set_drvdata(pdev, pcie); + pcie->dev = dev; INIT_LIST_HEAD(&pcie->resources); rcar_pcie_parse_request_of_pci_ranges(pcie); - err = rcar_pcie_get_resources(pdev, pcie); + err = rcar_pcie_get_resources(pcie); if (err < 0) { - dev_err(&pdev->dev, "failed to request resources: %d\n", err); + dev_err(dev, "failed to request resources: %d\n", err); return err; } - err = rcar_pcie_parse_map_dma_ranges(pcie, pdev->dev.of_node); + err = rcar_pcie_parse_map_dma_ranges(pcie, dev->of_node); if (err) return err; - of_id = of_match_device(rcar_pcie_of_match, pcie->dev); + of_id = of_match_device(rcar_pcie_of_match, dev); if (!of_id || !of_id->data) return -EINVAL; hw_init_fn = of_id->data; - pm_runtime_enable(pcie->dev); - err = pm_runtime_get_sync(pcie->dev); + pm_runtime_enable(dev); + err = pm_runtime_get_sync(dev); if (err < 0) { - dev_err(pcie->dev, "pm_runtime_get_sync failed\n"); + dev_err(dev, "pm_runtime_get_sync failed\n"); goto err_pm_disable; } /* Failure to get a link might just be that no cards are inserted */ err = hw_init_fn(pcie); if (err) { - dev_info(&pdev->dev, "PCIe link down\n"); + dev_info(dev, "PCIe link down\n"); err = 0; goto err_pm_put; } data = rcar_pci_read_reg(pcie, MACSR); - dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); + dev_info(dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f); if (IS_ENABLED(CONFIG_PCI_MSI)) { err = rcar_pcie_enable_msi(pcie); if (err < 0) { - dev_err(&pdev->dev, + dev_err(dev, "failed to enable MSI support: %d\n", err); goto err_pm_put; @@ -1186,16 +1188,16 @@ static int rcar_pcie_probe(struct platform_device *pdev) return 0; err_pm_put: - pm_runtime_put(pcie->dev); + pm_runtime_put(dev); err_pm_disable: - pm_runtime_disable(pcie->dev); + pm_runtime_disable(dev); return err; } static struct platform_driver rcar_pcie_driver = { .driver = { - .name = DRV_NAME, + .name = "rcar-pcie", .of_match_table = rcar_pcie_of_match, .suppress_bind_attrs = true, }, diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c index b8c82fc812dc..e0b22dab9b7a 100644 --- a/drivers/pci/host/pcie-rockchip.c +++ b/drivers/pci/host/pcie-rockchip.c @@ -972,7 +972,7 @@ static int rockchip_pcie_prog_ob_atu(struct rockchip_pcie *rockchip, return -EINVAL; if (region_no == 0) { if (AXI_REGION_0_SIZE < (2ULL << num_pass_bits)) - return -EINVAL; + return -EINVAL; } if (region_no != 0) { if (AXI_REGION_SIZE < (2ULL << num_pass_bits)) @@ -1091,8 +1091,6 @@ static int rockchip_pcie_probe(struct platform_device *pdev) if (err) goto err_vpcie; - platform_set_drvdata(pdev, rockchip); - rockchip_pcie_enable_interrupts(rockchip); err = rockchip_pcie_init_irq_domain(rockchip); diff --git a/drivers/pci/host/pcie-spear13xx.c b/drivers/pci/host/pcie-spear13xx.c index 09aed85f275a..3cf197ba7f37 100644 --- a/drivers/pci/host/pcie-spear13xx.c +++ b/drivers/pci/host/pcie-spear13xx.c @@ -25,10 +25,10 @@ #include "pcie-designware.h" struct spear13xx_pcie { + struct pcie_port pp; /* DT dbi is pp.dbi_base */ void __iomem *app_base; struct phy *phy; struct clk *clk; - struct pcie_port pp; bool is_gen1; }; @@ -57,96 +57,26 @@ struct pcie_app_reg { }; /* CR0 ID */ -#define RX_LANE_FLIP_EN_ID 0 -#define TX_LANE_FLIP_EN_ID 1 -#define SYS_AUX_PWR_DET_ID 2 #define APP_LTSSM_ENABLE_ID 3 -#define SYS_ATTEN_BUTTON_PRESSED_ID 4 -#define SYS_MRL_SENSOR_STATE_ID 5 -#define SYS_PWR_FAULT_DET_ID 6 -#define SYS_MRL_SENSOR_CHGED_ID 7 -#define SYS_PRE_DET_CHGED_ID 8 -#define SYS_CMD_CPLED_INT_ID 9 -#define APP_INIT_RST_0_ID 11 -#define APP_REQ_ENTR_L1_ID 12 -#define APP_READY_ENTR_L23_ID 13 -#define APP_REQ_EXIT_L1_ID 14 -#define DEVICE_TYPE_EP (0 << 25) -#define DEVICE_TYPE_LEP (1 << 25) #define DEVICE_TYPE_RC (4 << 25) -#define SYS_INT_ID 29 #define MISCTRL_EN_ID 30 #define REG_TRANSLATION_ENABLE 31 -/* CR1 ID */ -#define APPS_PM_XMT_TURNOFF_ID 2 -#define APPS_PM_XMT_PME_ID 5 - /* CR3 ID */ -#define XMLH_LTSSM_STATE_DETECT_QUIET 0x00 -#define XMLH_LTSSM_STATE_DETECT_ACT 0x01 -#define XMLH_LTSSM_STATE_POLL_ACTIVE 0x02 -#define XMLH_LTSSM_STATE_POLL_COMPLIANCE 0x03 -#define XMLH_LTSSM_STATE_POLL_CONFIG 0x04 -#define XMLH_LTSSM_STATE_PRE_DETECT_QUIET 0x05 -#define XMLH_LTSSM_STATE_DETECT_WAIT 0x06 -#define XMLH_LTSSM_STATE_CFG_LINKWD_START 0x07 -#define XMLH_LTSSM_STATE_CFG_LINKWD_ACEPT 0x08 -#define XMLH_LTSSM_STATE_CFG_LANENUM_WAIT 0x09 -#define XMLH_LTSSM_STATE_CFG_LANENUM_ACEPT 0x0A -#define XMLH_LTSSM_STATE_CFG_COMPLETE 0x0B -#define XMLH_LTSSM_STATE_CFG_IDLE 0x0C -#define XMLH_LTSSM_STATE_RCVRY_LOCK 0x0D -#define XMLH_LTSSM_STATE_RCVRY_SPEED 0x0E -#define XMLH_LTSSM_STATE_RCVRY_RCVRCFG 0x0F -#define XMLH_LTSSM_STATE_RCVRY_IDLE 0x10 -#define XMLH_LTSSM_STATE_L0 0x11 -#define XMLH_LTSSM_STATE_L0S 0x12 -#define XMLH_LTSSM_STATE_L123_SEND_EIDLE 0x13 -#define XMLH_LTSSM_STATE_L1_IDLE 0x14 -#define XMLH_LTSSM_STATE_L2_IDLE 0x15 -#define XMLH_LTSSM_STATE_L2_WAKE 0x16 -#define XMLH_LTSSM_STATE_DISABLED_ENTRY 0x17 -#define XMLH_LTSSM_STATE_DISABLED_IDLE 0x18 -#define XMLH_LTSSM_STATE_DISABLED 0x19 -#define XMLH_LTSSM_STATE_LPBK_ENTRY 0x1A -#define XMLH_LTSSM_STATE_LPBK_ACTIVE 0x1B -#define XMLH_LTSSM_STATE_LPBK_EXIT 0x1C -#define XMLH_LTSSM_STATE_LPBK_EXIT_TIMEOUT 0x1D -#define XMLH_LTSSM_STATE_HOT_RESET_ENTRY 0x1E -#define XMLH_LTSSM_STATE_HOT_RESET 0x1F -#define XMLH_LTSSM_STATE_MASK 0x3F #define XMLH_LINK_UP (1 << 6) -/* CR4 ID */ -#define CFG_MSI_EN_ID 18 - /* CR6 */ -#define INTA_CTRL_INT (1 << 7) -#define INTB_CTRL_INT (1 << 8) -#define INTC_CTRL_INT (1 << 9) -#define INTD_CTRL_INT (1 << 10) #define MSI_CTRL_INT (1 << 26) -/* CR19 ID */ -#define VEN_MSI_REQ_ID 11 -#define VEN_MSI_FUN_NUM_ID 8 -#define VEN_MSI_TC_ID 5 -#define VEN_MSI_VECTOR_ID 0 -#define VEN_MSI_REQ_EN ((u32)0x1 << VEN_MSI_REQ_ID) -#define VEN_MSI_FUN_NUM_MASK ((u32)0x7 << VEN_MSI_FUN_NUM_ID) -#define VEN_MSI_TC_MASK ((u32)0x7 << VEN_MSI_TC_ID) -#define VEN_MSI_VECTOR_MASK ((u32)0x1F << VEN_MSI_VECTOR_ID) - #define EXP_CAP_ID_OFFSET 0x70 #define to_spear13xx_pcie(x) container_of(x, struct spear13xx_pcie, pp) -static int spear13xx_pcie_establish_link(struct pcie_port *pp) +static int spear13xx_pcie_establish_link(struct spear13xx_pcie *spear13xx_pcie) { - u32 val; - struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp); + struct pcie_port *pp = &spear13xx_pcie->pp; struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; + u32 val; u32 exp_cap_off = EXP_CAP_ID_OFFSET; if (dw_pcie_link_up(pp)) { @@ -203,9 +133,9 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp) static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg) { - struct pcie_port *pp = arg; - struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp); + struct spear13xx_pcie *spear13xx_pcie = arg; struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; + struct pcie_port *pp = &spear13xx_pcie->pp; unsigned int status; status = readl(&app_reg->int_sts); @@ -220,9 +150,9 @@ static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg) return IRQ_HANDLED; } -static void spear13xx_pcie_enable_interrupts(struct pcie_port *pp) +static void spear13xx_pcie_enable_interrupts(struct spear13xx_pcie *spear13xx_pcie) { - struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp); + struct pcie_port *pp = &spear13xx_pcie->pp; struct pcie_app_reg *app_reg = spear13xx_pcie->app_base; /* Enable MSI interrupt */ @@ -246,8 +176,10 @@ static int spear13xx_pcie_link_up(struct pcie_port *pp) static void spear13xx_pcie_host_init(struct pcie_port *pp) { - spear13xx_pcie_establish_link(pp); - spear13xx_pcie_enable_interrupts(pp); + struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp); + + spear13xx_pcie_establish_link(spear13xx_pcie); + spear13xx_pcie_enable_interrupts(spear13xx_pcie); } static struct pcie_host_ops spear13xx_pcie_host_ops = { @@ -255,10 +187,11 @@ static struct pcie_host_ops spear13xx_pcie_host_ops = { .host_init = spear13xx_pcie_host_init, }; -static int spear13xx_add_pcie_port(struct pcie_port *pp, - struct platform_device *pdev) +static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie, + struct platform_device *pdev) { - struct device *dev = &pdev->dev; + struct pcie_port *pp = &spear13xx_pcie->pp; + struct device *dev = pp->dev; int ret; pp->irq = platform_get_irq(pdev, 0); @@ -268,7 +201,7 @@ static int spear13xx_add_pcie_port(struct pcie_port *pp, } ret = devm_request_irq(dev, pp->irq, spear13xx_pcie_irq_handler, IRQF_SHARED | IRQF_NO_THREAD, - "spear1340-pcie", pp); + "spear1340-pcie", spear13xx_pcie); if (ret) { dev_err(dev, "failed to request irq %d\n", pp->irq); return ret; @@ -288,10 +221,10 @@ static int spear13xx_add_pcie_port(struct pcie_port *pp, static int spear13xx_pcie_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct spear13xx_pcie *spear13xx_pcie; struct pcie_port *pp; - struct device *dev = &pdev->dev; - struct device_node *np = pdev->dev.of_node; + struct device_node *np = dev->of_node; struct resource *dbi_base; int ret; @@ -323,7 +256,6 @@ static int spear13xx_pcie_probe(struct platform_device *pdev) } pp = &spear13xx_pcie->pp; - pp->dev = dev; dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); @@ -338,7 +270,7 @@ static int spear13xx_pcie_probe(struct platform_device *pdev) if (of_property_read_bool(np, "st,pcie-is-gen1")) spear13xx_pcie->is_gen1 = true; - ret = spear13xx_add_pcie_port(pp, pdev); + ret = spear13xx_add_pcie_port(spear13xx_pcie, pdev); if (ret < 0) goto fail_clk; diff --git a/drivers/pci/host/pcie-xilinx-nwl.c b/drivers/pci/host/pcie-xilinx-nwl.c index 67eae4179290..43eaa4afab94 100644 --- a/drivers/pci/host/pcie-xilinx-nwl.c +++ b/drivers/pci/host/pcie-xilinx-nwl.c @@ -212,6 +212,7 @@ static bool nwl_phy_link_up(struct nwl_pcie *pcie) static int nwl_wait_for_link(struct nwl_pcie *pcie) { + struct device *dev = pcie->dev; int retries; /* check if the link is up or not */ @@ -221,7 +222,7 @@ static int nwl_wait_for_link(struct nwl_pcie *pcie) usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); } - dev_err(pcie->dev, "PHY link never came up\n"); + dev_err(dev, "PHY link never came up\n"); return -ETIMEDOUT; } @@ -277,6 +278,7 @@ static struct pci_ops nwl_pcie_ops = { static irqreturn_t nwl_pcie_misc_handler(int irq, void *data) { struct nwl_pcie *pcie = data; + struct device *dev = pcie->dev; u32 misc_stat; /* Checking for misc interrupts */ @@ -286,45 +288,43 @@ static irqreturn_t nwl_pcie_misc_handler(int irq, void *data) return IRQ_NONE; if (misc_stat & MSGF_MISC_SR_RXMSG_OVER) - dev_err(pcie->dev, "Received Message FIFO Overflow\n"); + dev_err(dev, "Received Message FIFO Overflow\n"); if (misc_stat & MSGF_MISC_SR_SLAVE_ERR) - dev_err(pcie->dev, "Slave error\n"); + dev_err(dev, "Slave error\n"); if (misc_stat & MSGF_MISC_SR_MASTER_ERR) - dev_err(pcie->dev, "Master error\n"); + dev_err(dev, "Master error\n"); if (misc_stat & MSGF_MISC_SR_I_ADDR_ERR) - dev_err(pcie->dev, - "In Misc Ingress address translation error\n"); + dev_err(dev, "In Misc Ingress address translation error\n"); if (misc_stat & MSGF_MISC_SR_E_ADDR_ERR) - dev_err(pcie->dev, - "In Misc Egress address translation error\n"); + dev_err(dev, "In Misc Egress address translation error\n"); if (misc_stat & MSGF_MISC_SR_FATAL_AER) - dev_err(pcie->dev, "Fatal Error in AER Capability\n"); + dev_err(dev, "Fatal Error in AER Capability\n"); if (misc_stat & MSGF_MISC_SR_NON_FATAL_AER) - dev_err(pcie->dev, "Non-Fatal Error in AER Capability\n"); + dev_err(dev, "Non-Fatal Error in AER Capability\n"); if (misc_stat & MSGF_MISC_SR_CORR_AER) - dev_err(pcie->dev, "Correctable Error in AER Capability\n"); + dev_err(dev, "Correctable Error in AER Capability\n"); if (misc_stat & MSGF_MISC_SR_UR_DETECT) - dev_err(pcie->dev, "Unsupported request Detected\n"); + dev_err(dev, "Unsupported request Detected\n"); if (misc_stat & MSGF_MISC_SR_NON_FATAL_DEV) - dev_err(pcie->dev, "Non-Fatal Error Detected\n"); + dev_err(dev, "Non-Fatal Error Detected\n"); if (misc_stat & MSGF_MISC_SR_FATAL_DEV) - dev_err(pcie->dev, "Fatal Error Detected\n"); + dev_err(dev, "Fatal Error Detected\n"); if (misc_stat & MSGF_MSIC_SR_LINK_AUTO_BWIDTH) - dev_info(pcie->dev, "Link Autonomous Bandwidth Management Status bit set\n"); + dev_info(dev, "Link Autonomous Bandwidth Management Status bit set\n"); if (misc_stat & MSGF_MSIC_SR_LINK_BWIDTH) - dev_info(pcie->dev, "Link Bandwidth Management Status bit set\n"); + dev_info(dev, "Link Bandwidth Management Status bit set\n"); /* Clear misc interrupt status */ nwl_bridge_writel(pcie, misc_stat, MSGF_MISC_STATUS); @@ -494,20 +494,21 @@ static const struct irq_domain_ops dev_msi_domain_ops = { static int nwl_pcie_init_msi_irq_domain(struct nwl_pcie *pcie) { #ifdef CONFIG_PCI_MSI - struct fwnode_handle *fwnode = of_node_to_fwnode(pcie->dev->of_node); + struct device *dev = pcie->dev; + struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); struct nwl_msi *msi = &pcie->msi; msi->dev_domain = irq_domain_add_linear(NULL, INT_PCI_MSI_NR, &dev_msi_domain_ops, pcie); if (!msi->dev_domain) { - dev_err(pcie->dev, "failed to create dev IRQ domain\n"); + dev_err(dev, "failed to create dev IRQ domain\n"); return -ENOMEM; } msi->msi_domain = pci_msi_create_irq_domain(fwnode, &nwl_msi_domain_info, msi->dev_domain); if (!msi->msi_domain) { - dev_err(pcie->dev, "failed to create msi IRQ domain\n"); + dev_err(dev, "failed to create msi IRQ domain\n"); irq_domain_remove(msi->dev_domain); return -ENOMEM; } @@ -517,12 +518,13 @@ static int nwl_pcie_init_msi_irq_domain(struct nwl_pcie *pcie) static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie) { - struct device_node *node = pcie->dev->of_node; + struct device *dev = pcie->dev; + struct device_node *node = dev->of_node; struct device_node *legacy_intc_node; legacy_intc_node = of_get_next_child(node, NULL); if (!legacy_intc_node) { - dev_err(pcie->dev, "No legacy intc node found\n"); + dev_err(dev, "No legacy intc node found\n"); return -EINVAL; } @@ -532,7 +534,7 @@ static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie) pcie); if (!pcie->legacy_irq_domain) { - dev_err(pcie->dev, "failed to create IRQ domain\n"); + dev_err(dev, "failed to create IRQ domain\n"); return -ENOMEM; } @@ -542,7 +544,8 @@ static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie) static int nwl_pcie_enable_msi(struct nwl_pcie *pcie, struct pci_bus *bus) { - struct platform_device *pdev = to_platform_device(pcie->dev); + struct device *dev = pcie->dev; + struct platform_device *pdev = to_platform_device(dev); struct nwl_msi *msi = &pcie->msi; unsigned long base; int ret; @@ -557,7 +560,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie, struct pci_bus *bus) /* Get msi_1 IRQ number */ msi->irq_msi1 = platform_get_irq_byname(pdev, "msi1"); if (msi->irq_msi1 < 0) { - dev_err(&pdev->dev, "failed to get IRQ#%d\n", msi->irq_msi1); + dev_err(dev, "failed to get IRQ#%d\n", msi->irq_msi1); ret = -EINVAL; goto err; } @@ -568,7 +571,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie, struct pci_bus *bus) /* Get msi_0 IRQ number */ msi->irq_msi0 = platform_get_irq_byname(pdev, "msi0"); if (msi->irq_msi0 < 0) { - dev_err(&pdev->dev, "failed to get IRQ#%d\n", msi->irq_msi0); + dev_err(dev, "failed to get IRQ#%d\n", msi->irq_msi0); ret = -EINVAL; goto err; } @@ -579,7 +582,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie, struct pci_bus *bus) /* Check for msii_present bit */ ret = nwl_bridge_readl(pcie, I_MSII_CAPABILITIES) & MSII_PRESENT; if (!ret) { - dev_err(pcie->dev, "MSI not present\n"); + dev_err(dev, "MSI not present\n"); ret = -EIO; goto err; } @@ -628,13 +631,14 @@ err: static int nwl_pcie_bridge_init(struct nwl_pcie *pcie) { - struct platform_device *pdev = to_platform_device(pcie->dev); + struct device *dev = pcie->dev; + struct platform_device *pdev = to_platform_device(dev); u32 breg_val, ecam_val, first_busno = 0; int err; breg_val = nwl_bridge_readl(pcie, E_BREG_CAPABILITIES) & BREG_PRESENT; if (!breg_val) { - dev_err(pcie->dev, "BREG is not present\n"); + dev_err(dev, "BREG is not present\n"); return breg_val; } @@ -665,7 +669,7 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie) ecam_val = nwl_bridge_readl(pcie, E_ECAM_CAPABILITIES) & E_ECAM_PRESENT; if (!ecam_val) { - dev_err(pcie->dev, "ECAM is not present\n"); + dev_err(dev, "ECAM is not present\n"); return ecam_val; } @@ -692,23 +696,23 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie) writel(ecam_val, (pcie->ecam_base + PCI_PRIMARY_BUS)); if (nwl_pcie_link_up(pcie)) - dev_info(pcie->dev, "Link is UP\n"); + dev_info(dev, "Link is UP\n"); else - dev_info(pcie->dev, "Link is DOWN\n"); + dev_info(dev, "Link is DOWN\n"); /* Get misc IRQ number */ pcie->irq_misc = platform_get_irq_byname(pdev, "misc"); if (pcie->irq_misc < 0) { - dev_err(&pdev->dev, "failed to get misc IRQ %d\n", + dev_err(dev, "failed to get misc IRQ %d\n", pcie->irq_misc); return -EINVAL; } - err = devm_request_irq(pcie->dev, pcie->irq_misc, + err = devm_request_irq(dev, pcie->irq_misc, nwl_pcie_misc_handler, IRQF_SHARED, "nwl_pcie:misc", pcie); if (err) { - dev_err(pcie->dev, "fail to register misc IRQ#%d\n", + dev_err(dev, "fail to register misc IRQ#%d\n", pcie->irq_misc); return err; } @@ -744,31 +748,32 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie) static int nwl_pcie_parse_dt(struct nwl_pcie *pcie, struct platform_device *pdev) { - struct device_node *node = pcie->dev->of_node; + struct device *dev = pcie->dev; + struct device_node *node = dev->of_node; struct resource *res; const char *type; /* Check for device type */ type = of_get_property(node, "device_type", NULL); if (!type || strcmp(type, "pci")) { - dev_err(pcie->dev, "invalid \"device_type\" %s\n", type); + dev_err(dev, "invalid \"device_type\" %s\n", type); return -EINVAL; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "breg"); - pcie->breg_base = devm_ioremap_resource(pcie->dev, res); + pcie->breg_base = devm_ioremap_resource(dev, res); if (IS_ERR(pcie->breg_base)) return PTR_ERR(pcie->breg_base); pcie->phys_breg_base = res->start; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcireg"); - pcie->pcireg_base = devm_ioremap_resource(pcie->dev, res); + pcie->pcireg_base = devm_ioremap_resource(dev, res); if (IS_ERR(pcie->pcireg_base)) return PTR_ERR(pcie->pcireg_base); pcie->phys_pcie_reg_base = res->start; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg"); - pcie->ecam_base = devm_ioremap_resource(pcie->dev, res); + pcie->ecam_base = devm_ioremap_resource(dev, res); if (IS_ERR(pcie->ecam_base)) return PTR_ERR(pcie->ecam_base); pcie->phys_ecam_base = res->start; @@ -776,8 +781,7 @@ static int nwl_pcie_parse_dt(struct nwl_pcie *pcie, /* Get intx IRQ number */ pcie->irq_intx = platform_get_irq_byname(pdev, "intx"); if (pcie->irq_intx < 0) { - dev_err(&pdev->dev, "failed to get intx IRQ %d\n", - pcie->irq_intx); + dev_err(dev, "failed to get intx IRQ %d\n", pcie->irq_intx); return -EINVAL; } @@ -794,7 +798,8 @@ static const struct of_device_id nwl_pcie_of_match[] = { static int nwl_pcie_probe(struct platform_device *pdev) { - struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; struct nwl_pcie *pcie; struct pci_bus *bus; struct pci_bus *child; @@ -802,42 +807,42 @@ static int nwl_pcie_probe(struct platform_device *pdev) resource_size_t iobase = 0; LIST_HEAD(res); - pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) return -ENOMEM; - pcie->dev = &pdev->dev; + pcie->dev = dev; pcie->ecam_value = NWL_ECAM_VALUE_DEFAULT; err = nwl_pcie_parse_dt(pcie, pdev); if (err) { - dev_err(pcie->dev, "Parsing DT failed\n"); + dev_err(dev, "Parsing DT failed\n"); return err; } err = nwl_pcie_bridge_init(pcie); if (err) { - dev_err(pcie->dev, "HW Initialization failed\n"); + dev_err(dev, "HW Initialization failed\n"); return err; } err = of_pci_get_host_bridge_resources(node, 0, 0xff, &res, &iobase); if (err) { - dev_err(pcie->dev, "Getting bridge resources failed\n"); + dev_err(dev, "Getting bridge resources failed\n"); return err; } - err = devm_request_pci_bus_resources(pcie->dev, &res); + err = devm_request_pci_bus_resources(dev, &res); if (err) goto error; err = nwl_pcie_init_irq_domain(pcie); if (err) { - dev_err(pcie->dev, "Failed creating IRQ Domain\n"); + dev_err(dev, "Failed creating IRQ Domain\n"); goto error; } - bus = pci_create_root_bus(&pdev->dev, pcie->root_busno, + bus = pci_create_root_bus(dev, pcie->root_busno, &nwl_pcie_ops, pcie, &res); if (!bus) { err = -ENOMEM; @@ -847,8 +852,7 @@ static int nwl_pcie_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_PCI_MSI)) { err = nwl_pcie_enable_msi(pcie, bus); if (err < 0) { - dev_err(&pdev->dev, - "failed to enable MSI support: %d\n", err); + dev_err(dev, "failed to enable MSI support: %d\n", err); goto error; } } @@ -857,7 +861,6 @@ static int nwl_pcie_probe(struct platform_device *pdev) list_for_each_entry(child, &bus->children, node) pcie_bus_configure_settings(child); pci_bus_add_devices(bus); - platform_set_drvdata(pdev, pcie); return 0; error: diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c index be568039d9d0..c8616fadccf1 100644 --- a/drivers/pci/host/pcie-xilinx.c +++ b/drivers/pci/host/pcie-xilinx.c @@ -140,10 +140,11 @@ static inline bool xilinx_pcie_link_is_up(struct xilinx_pcie_port *port) */ static void xilinx_pcie_clear_err_interrupts(struct xilinx_pcie_port *port) { + struct device *dev = port->dev; unsigned long val = pcie_read(port, XILINX_PCIE_REG_RPEFR); if (val & XILINX_PCIE_RPEFR_ERR_VALID) { - dev_dbg(port->dev, "Requester ID %lu\n", + dev_dbg(dev, "Requester ID %lu\n", val & XILINX_PCIE_RPEFR_REQ_ID); pcie_write(port, XILINX_PCIE_RPEFR_ALL_MASK, XILINX_PCIE_REG_RPEFR); @@ -228,11 +229,10 @@ static void xilinx_pcie_destroy_msi(unsigned int irq) /** * xilinx_pcie_assign_msi - Allocate MSI number - * @port: PCIe port structure * * Return: A valid IRQ on success and error value on failure. */ -static int xilinx_pcie_assign_msi(struct xilinx_pcie_port *port) +static int xilinx_pcie_assign_msi(void) { int pos; @@ -275,7 +275,7 @@ static int xilinx_pcie_msi_setup_irq(struct msi_controller *chip, struct msi_msg msg; phys_addr_t msg_addr; - hwirq = xilinx_pcie_assign_msi(port); + hwirq = xilinx_pcie_assign_msi(); if (hwirq < 0) return hwirq; @@ -383,6 +383,7 @@ static const struct irq_domain_ops intx_domain_ops = { static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data) { struct xilinx_pcie_port *port = (struct xilinx_pcie_port *)data; + struct device *dev = port->dev; u32 val, mask, status, msi_data; /* Read interrupt decode and mask registers */ @@ -394,32 +395,32 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data) return IRQ_NONE; if (status & XILINX_PCIE_INTR_LINK_DOWN) - dev_warn(port->dev, "Link Down\n"); + dev_warn(dev, "Link Down\n"); if (status & XILINX_PCIE_INTR_ECRC_ERR) - dev_warn(port->dev, "ECRC failed\n"); + dev_warn(dev, "ECRC failed\n"); if (status & XILINX_PCIE_INTR_STR_ERR) - dev_warn(port->dev, "Streaming error\n"); + dev_warn(dev, "Streaming error\n"); if (status & XILINX_PCIE_INTR_HOT_RESET) - dev_info(port->dev, "Hot reset\n"); + dev_info(dev, "Hot reset\n"); if (status & XILINX_PCIE_INTR_CFG_TIMEOUT) - dev_warn(port->dev, "ECAM access timeout\n"); + dev_warn(dev, "ECAM access timeout\n"); if (status & XILINX_PCIE_INTR_CORRECTABLE) { - dev_warn(port->dev, "Correctable error message\n"); + dev_warn(dev, "Correctable error message\n"); xilinx_pcie_clear_err_interrupts(port); } if (status & XILINX_PCIE_INTR_NONFATAL) { - dev_warn(port->dev, "Non fatal error message\n"); + dev_warn(dev, "Non fatal error message\n"); xilinx_pcie_clear_err_interrupts(port); } if (status & XILINX_PCIE_INTR_FATAL) { - dev_warn(port->dev, "Fatal error message\n"); + dev_warn(dev, "Fatal error message\n"); xilinx_pcie_clear_err_interrupts(port); } @@ -429,7 +430,7 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data) /* Check whether interrupt valid */ if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) { - dev_warn(port->dev, "RP Intr FIFO1 read error\n"); + dev_warn(dev, "RP Intr FIFO1 read error\n"); goto error; } @@ -451,7 +452,7 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data) val = pcie_read(port, XILINX_PCIE_REG_RPIFR1); if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) { - dev_warn(port->dev, "RP Intr FIFO1 read error\n"); + dev_warn(dev, "RP Intr FIFO1 read error\n"); goto error; } @@ -471,31 +472,31 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data) } if (status & XILINX_PCIE_INTR_SLV_UNSUPP) - dev_warn(port->dev, "Slave unsupported request\n"); + dev_warn(dev, "Slave unsupported request\n"); if (status & XILINX_PCIE_INTR_SLV_UNEXP) - dev_warn(port->dev, "Slave unexpected completion\n"); + dev_warn(dev, "Slave unexpected completion\n"); if (status & XILINX_PCIE_INTR_SLV_COMPL) - dev_warn(port->dev, "Slave completion timeout\n"); + dev_warn(dev, "Slave completion timeout\n"); if (status & XILINX_PCIE_INTR_SLV_ERRP) - dev_warn(port->dev, "Slave Error Poison\n"); + dev_warn(dev, "Slave Error Poison\n"); if (status & XILINX_PCIE_INTR_SLV_CMPABT) - dev_warn(port->dev, "Slave Completer Abort\n"); + dev_warn(dev, "Slave Completer Abort\n"); if (status & XILINX_PCIE_INTR_SLV_ILLBUR) - dev_warn(port->dev, "Slave Illegal Burst\n"); + dev_warn(dev, "Slave Illegal Burst\n"); if (status & XILINX_PCIE_INTR_MST_DECERR) - dev_warn(port->dev, "Master decode error\n"); + dev_warn(dev, "Master decode error\n"); if (status & XILINX_PCIE_INTR_MST_SLVERR) - dev_warn(port->dev, "Master slave error\n"); + dev_warn(dev, "Master slave error\n"); if (status & XILINX_PCIE_INTR_MST_ERRP) - dev_warn(port->dev, "Master error poison\n"); + dev_warn(dev, "Master error poison\n"); error: /* Clear the Interrupt Decode register */ @@ -554,10 +555,12 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port) */ static void xilinx_pcie_init_port(struct xilinx_pcie_port *port) { + struct device *dev = port->dev; + if (xilinx_pcie_link_is_up(port)) - dev_info(port->dev, "PCIe Link is UP\n"); + dev_info(dev, "PCIe Link is UP\n"); else - dev_info(port->dev, "PCIe Link is DOWN\n"); + dev_info(dev, "PCIe Link is DOWN\n"); /* Disable all interrupts */ pcie_write(port, ~XILINX_PCIE_IDR_ALL_MASK, @@ -627,8 +630,8 @@ static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port) */ static int xilinx_pcie_probe(struct platform_device *pdev) { - struct xilinx_pcie_port *port; struct device *dev = &pdev->dev; + struct xilinx_pcie_port *port; struct pci_bus *bus; int err; resource_size_t iobase = 0; @@ -668,15 +671,14 @@ static int xilinx_pcie_probe(struct platform_device *pdev) if (err) goto error; - bus = pci_create_root_bus(&pdev->dev, 0, - &xilinx_pcie_ops, port, &res); + bus = pci_create_root_bus(dev, 0, &xilinx_pcie_ops, port, &res); if (!bus) { err = -ENOMEM; goto error; } #ifdef CONFIG_PCI_MSI - xilinx_pcie_msi_chip.dev = port->dev; + xilinx_pcie_msi_chip.dev = dev; bus->msi = &xilinx_pcie_msi_chip; #endif pci_scan_child_bus(bus); @@ -685,8 +687,6 @@ static int xilinx_pcie_probe(struct platform_device *pdev) pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); #endif pci_bus_add_devices(bus); - platform_set_drvdata(pdev, port); - return 0; error: diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 460fa6708bfc..2acdb0d6ea89 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -405,7 +405,7 @@ static inline void acerhdf_enable_kernelmode(void) kernelmode = 1; thz_dev->polling_delay = interval*1000; - thermal_zone_device_update(thz_dev); + thermal_zone_device_update(thz_dev, THERMAL_EVENT_UNSPECIFIED); pr_notice("kernel mode fan control ON\n"); } diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 15f131146501..28551f5a2e07 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -932,30 +932,19 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(infos); -static int parse_arg(const char *buf, unsigned long count, int *val) -{ - if (!count) - return 0; - if (count > 31) - return -EINVAL; - if (sscanf(buf, "%i", val) != 1) - return -EINVAL; - return count; -} - static ssize_t sysfs_acpi_set(struct asus_laptop *asus, const char *buf, size_t count, const char *method) { int rv, value; - rv = parse_arg(buf, count, &value); - if (rv <= 0) + rv = kstrtoint(buf, 0, &value); + if (rv < 0) return rv; if (write_acpi_int(asus->handle, method, value)) return -ENODEV; - return rv; + return count; } /* @@ -975,15 +964,17 @@ static ssize_t ledd_store(struct device *dev, struct device_attribute *attr, struct asus_laptop *asus = dev_get_drvdata(dev); int rv, value; - rv = parse_arg(buf, count, &value); - if (rv > 0) { - if (write_acpi_int(asus->handle, METHOD_LEDD, value)) { - pr_warn("LED display write failed\n"); - return -ENODEV; - } - asus->ledd_status = (u32) value; + rv = kstrtoint(buf, 0, &value); + if (rv < 0) + return rv; + + if (write_acpi_int(asus->handle, METHOD_LEDD, value)) { + pr_warn("LED display write failed\n"); + return -ENODEV; } - return rv; + + asus->ledd_status = (u32) value; + return count; } static DEVICE_ATTR_RW(ledd); @@ -1148,10 +1139,12 @@ static ssize_t display_store(struct device *dev, struct device_attribute *attr, struct asus_laptop *asus = dev_get_drvdata(dev); int rv, value; - rv = parse_arg(buf, count, &value); - if (rv > 0) - asus_set_display(asus, value); - return rv; + rv = kstrtoint(buf, 0, &value); + if (rv < 0) + return rv; + + asus_set_display(asus, value); + return count; } static DEVICE_ATTR_WO(display); @@ -1190,11 +1183,12 @@ static ssize_t ls_switch_store(struct device *dev, struct asus_laptop *asus = dev_get_drvdata(dev); int rv, value; - rv = parse_arg(buf, count, &value); - if (rv > 0) - asus_als_switch(asus, value ? 1 : 0); + rv = kstrtoint(buf, 0, &value); + if (rv < 0) + return rv; - return rv; + asus_als_switch(asus, value ? 1 : 0); + return count; } static DEVICE_ATTR_RW(ls_switch); @@ -1219,14 +1213,15 @@ static ssize_t ls_level_store(struct device *dev, struct device_attribute *attr, struct asus_laptop *asus = dev_get_drvdata(dev); int rv, value; - rv = parse_arg(buf, count, &value); - if (rv > 0) { - value = (0 < value) ? ((15 < value) ? 15 : value) : 0; - /* 0 <= value <= 15 */ - asus_als_level(asus, value); - } + rv = kstrtoint(buf, 0, &value); + if (rv < 0) + return rv; + + value = (0 < value) ? ((15 < value) ? 15 : value) : 0; + /* 0 <= value <= 15 */ + asus_als_level(asus, value); - return rv; + return count; } static DEVICE_ATTR_RW(ls_level); @@ -1301,14 +1296,14 @@ static ssize_t gps_store(struct device *dev, struct device_attribute *attr, int rv, value; int ret; - rv = parse_arg(buf, count, &value); - if (rv <= 0) - return -EINVAL; + rv = kstrtoint(buf, 0, &value); + if (rv < 0) + return rv; ret = asus_gps_switch(asus, !!value); if (ret) return ret; rfkill_set_sw_state(asus->gps.rfkill, !value); - return rv; + return count; } static DEVICE_ATTR_RW(gps); diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index adecc1c555f0..26e4cbc34db8 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -27,6 +27,7 @@ #include <linux/input/sparse-keymap.h> #include <linux/fb.h> #include <linux/dmi.h> +#include <linux/i8042.h> #include "asus-wmi.h" @@ -55,10 +56,34 @@ MODULE_PARM_DESC(wapf, "WAPF value"); static struct quirk_entry *quirks; +static bool asus_q500a_i8042_filter(unsigned char data, unsigned char str, + struct serio *port) +{ + static bool extended; + bool ret = false; + + if (str & I8042_STR_AUXDATA) + return false; + + if (unlikely(data == 0xe1)) { + extended = true; + ret = true; + } else if (unlikely(extended)) { + extended = false; + ret = true; + } + + return ret; +} + static struct quirk_entry quirk_asus_unknown = { .wapf = 0, }; +static struct quirk_entry quirk_asus_q500a = { + .i8042_filter = asus_q500a_i8042_filter, +}; + /* * For those machines that need software to control bt/wifi status * and can't adjust brightness through ACPI interface @@ -87,6 +112,10 @@ static struct quirk_entry quirk_no_rfkill_wapf4 = { .no_rfkill = true, }; +static struct quirk_entry quirk_asus_ux303ub = { + .wmi_backlight_native = true, +}; + static int dmi_matched(const struct dmi_system_id *dmi) { quirks = dmi->driver_data; @@ -96,6 +125,15 @@ static int dmi_matched(const struct dmi_system_id *dmi) static const struct dmi_system_id asus_quirks[] = { { .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. Q500A", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "Q500A"), + }, + .driver_data = &quirk_asus_q500a, + }, + { + .callback = dmi_matched, .ident = "ASUSTeK COMPUTER INC. U32U", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), @@ -351,11 +389,22 @@ static const struct dmi_system_id asus_quirks[] = { }, .driver_data = &quirk_no_rfkill, }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. UX303UB", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "UX303UB"), + }, + .driver_data = &quirk_asus_ux303ub, + }, {}, }; static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) { + int ret; + quirks = &quirk_asus_unknown; dmi_check_system(asus_quirks); @@ -367,6 +416,15 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) quirks->wapf = wapf; else wapf = quirks->wapf; + + if (quirks->i8042_filter) { + ret = i8042_install_filter(quirks->i8042_filter); + if (ret) { + pr_warn("Unable to install key filter\n"); + return; + } + pr_info("Using i8042 filter function for receiving events\n"); + } } static const struct key_entry asus_nb_wmi_keymap[] = { diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 7c093a0b78bb..ce6ca31a2d09 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -2084,6 +2084,9 @@ static int asus_wmi_add(struct platform_device *pdev) if (asus->driver->quirks->wmi_backlight_power) acpi_video_set_dmi_backlight_type(acpi_backlight_vendor); + if (asus->driver->quirks->wmi_backlight_native) + acpi_video_set_dmi_backlight_type(acpi_backlight_native); + if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { err = asus_wmi_backlight_init(asus); if (err && err != -ENODEV) diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index 5de1df510ebd..0e19014e9f54 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -28,6 +28,7 @@ #define _ASUS_WMI_H_ #include <linux/platform_device.h> +#include <linux/i8042.h> #define ASUS_WMI_KEY_IGNORE (-1) #define ASUS_WMI_BRN_DOWN 0x20 @@ -43,6 +44,7 @@ struct quirk_entry { bool scalar_panel_brightness; bool store_backlight_power; bool wmi_backlight_power; + bool wmi_backlight_native; int wapf; /* * For machines with AMD graphic chips, it will send out WMI event @@ -51,6 +53,9 @@ struct quirk_entry { * and let the ACPI interrupt to send out the key event. */ int no_display_toggle; + + bool (*i8042_filter)(unsigned char data, unsigned char str, + struct serio *serio); }; struct asus_wmi_driver { diff --git a/drivers/platform/x86/dell-smo8800.c b/drivers/platform/x86/dell-smo8800.c index 0aec4fd4c48e..37e646034ef8 100644 --- a/drivers/platform/x86/dell-smo8800.c +++ b/drivers/platform/x86/dell-smo8800.c @@ -24,6 +24,7 @@ #include <linux/acpi.h> #include <linux/interrupt.h> #include <linux/miscdevice.h> +#include <linux/uaccess.h> struct smo8800_device { u32 irq; /* acpi device irq */ diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c index 520b58a04daa..e8b1b836ca2d 100644 --- a/drivers/platform/x86/intel_pmc_core.c +++ b/drivers/platform/x86/intel_pmc_core.c @@ -100,7 +100,7 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev) struct dentry *dir, *file; dir = debugfs_create_dir("pmc_core", NULL); - if (IS_ERR_OR_NULL(dir)) + if (!dir) return -ENOMEM; pmcdev->dbgfs_dir = dir; diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index a511d518206b..0bf51d574fa9 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -522,48 +522,36 @@ static struct resource telemetry_res[] = { static int ipc_create_punit_device(void) { struct platform_device *pdev; - int ret; - - pdev = platform_device_alloc(PUNIT_DEVICE_NAME, -1); - if (!pdev) { - dev_err(ipcdev.dev, "Failed to alloc punit platform device\n"); - return -ENOMEM; - } - - pdev->dev.parent = ipcdev.dev; - ret = platform_device_add_resources(pdev, punit_res_array, - ARRAY_SIZE(punit_res_array)); - if (ret) { - dev_err(ipcdev.dev, "Failed to add platform punit resources\n"); - goto err; - } + const struct platform_device_info pdevinfo = { + .parent = ipcdev.dev, + .name = PUNIT_DEVICE_NAME, + .id = -1, + .res = punit_res_array, + .num_res = ARRAY_SIZE(punit_res_array), + }; + + pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); - ret = platform_device_add(pdev); - if (ret) { - dev_err(ipcdev.dev, "Failed to add punit platform device\n"); - goto err; - } ipcdev.punit_dev = pdev; return 0; -err: - platform_device_put(pdev); - return ret; } static int ipc_create_tco_device(void) { struct platform_device *pdev; struct resource *res; - int ret; - - pdev = platform_device_alloc(TCO_DEVICE_NAME, -1); - if (!pdev) { - dev_err(ipcdev.dev, "Failed to alloc tco platform device\n"); - return -ENOMEM; - } - - pdev->dev.parent = ipcdev.dev; + const struct platform_device_info pdevinfo = { + .parent = ipcdev.dev, + .name = TCO_DEVICE_NAME, + .id = -1, + .res = tco_res, + .num_res = ARRAY_SIZE(tco_res), + .data = &tco_info, + .size_data = sizeof(tco_info), + }; res = tco_res + TCO_RESOURCE_ACPI_IO; res->start = ipcdev.acpi_io_base + TCO_BASE_OFFSET; @@ -577,45 +565,26 @@ static int ipc_create_tco_device(void) res->start = ipcdev.gcr_base + TCO_PMC_OFFSET; res->end = res->start + TCO_PMC_SIZE - 1; - ret = platform_device_add_resources(pdev, tco_res, ARRAY_SIZE(tco_res)); - if (ret) { - dev_err(ipcdev.dev, "Failed to add tco platform resources\n"); - goto err; - } + pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); - ret = platform_device_add_data(pdev, &tco_info, sizeof(tco_info)); - if (ret) { - dev_err(ipcdev.dev, "Failed to add tco platform data\n"); - goto err; - } - - ret = platform_device_add(pdev); - if (ret) { - dev_err(ipcdev.dev, "Failed to add tco platform device\n"); - goto err; - } ipcdev.tco_dev = pdev; return 0; -err: - platform_device_put(pdev); - return ret; } static int ipc_create_telemetry_device(void) { struct platform_device *pdev; struct resource *res; - int ret; - - pdev = platform_device_alloc(TELEMETRY_DEVICE_NAME, -1); - if (!pdev) { - dev_err(ipcdev.dev, - "Failed to allocate telemetry platform device\n"); - return -ENOMEM; - } - - pdev->dev.parent = ipcdev.dev; + const struct platform_device_info pdevinfo = { + .parent = ipcdev.dev, + .name = TELEMETRY_DEVICE_NAME, + .id = -1, + .res = telemetry_res, + .num_res = ARRAY_SIZE(telemetry_res), + }; res = telemetry_res + TELEMETRY_RESOURCE_PUNIT_SSRAM; res->start = ipcdev.telem_punit_ssram_base; @@ -625,26 +594,13 @@ static int ipc_create_telemetry_device(void) res->start = ipcdev.telem_pmc_ssram_base; res->end = res->start + ipcdev.telem_pmc_ssram_size - 1; - ret = platform_device_add_resources(pdev, telemetry_res, - ARRAY_SIZE(telemetry_res)); - if (ret) { - dev_err(ipcdev.dev, - "Failed to add telemetry platform resources\n"); - goto err; - } + pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); - ret = platform_device_add(pdev); - if (ret) { - dev_err(ipcdev.dev, - "Failed to add telemetry platform device\n"); - goto err; - } ipcdev.telemetry_dev = pdev; return 0; -err: - platform_device_put(pdev); - return ret; } static int ipc_create_pmc_devices(void) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 9d60a40d8b3f..074bf2fa1c55 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -321,10 +321,9 @@ static int write_acpi_int(const char *methodName, int val) static acpi_status tci_raw(struct toshiba_acpi_dev *dev, const u32 in[TCI_WORDS], u32 out[TCI_WORDS]) { + union acpi_object in_objs[TCI_WORDS], out_objs[TCI_WORDS + 1]; struct acpi_object_list params; - union acpi_object in_objs[TCI_WORDS]; struct acpi_buffer results; - union acpi_object out_objs[TCI_WORDS + 1]; acpi_status status; int i; @@ -387,9 +386,8 @@ static int sci_open(struct toshiba_acpi_dev *dev) { u32 in[TCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 }; u32 out[TCI_WORDS]; - acpi_status status; + acpi_status status = tci_raw(dev, in, out); - status = tci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_err("ACPI call to open SCI failed\n"); return 0; @@ -425,9 +423,8 @@ static void sci_close(struct toshiba_acpi_dev *dev) { u32 in[TCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 }; u32 out[TCI_WORDS]; - acpi_status status; + acpi_status status = tci_raw(dev, in, out); - status = tci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_err("ACPI call to close SCI failed\n"); return; @@ -479,10 +476,15 @@ static void toshiba_illumination_available(struct toshiba_acpi_dev *dev) status = tci_raw(dev, in, out); sci_close(dev); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { pr_err("ACPI call to query Illumination support failed\n"); - else if (out[0] == TOS_SUCCESS) - dev->illumination_supported = 1; + return; + } + + if (out[0] != TOS_SUCCESS) + return; + + dev->illumination_supported = 1; } static void toshiba_illumination_set(struct led_classdev *cdev, @@ -509,7 +511,8 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) { struct toshiba_acpi_dev *dev = container_of(cdev, struct toshiba_acpi_dev, led_dev); - u32 state, result; + u32 result; + u32 state; /* First request : initialize communication. */ if (!sci_open(dev)) @@ -546,24 +549,28 @@ static void toshiba_kbd_illum_available(struct toshiba_acpi_dev *dev) sci_close(dev); if (ACPI_FAILURE(status)) { pr_err("ACPI call to query kbd illumination support failed\n"); - } else if (out[0] == TOS_SUCCESS) { - /* - * Check for keyboard backlight timeout max value, - * previous kbd backlight implementation set this to - * 0x3c0003, and now the new implementation set this - * to 0x3c001a, use this to distinguish between them. - */ - if (out[3] == SCI_KBD_TIME_MAX) - dev->kbd_type = 2; - else - dev->kbd_type = 1; - /* Get the current keyboard backlight mode */ - dev->kbd_mode = out[2] & SCI_KBD_MODE_MASK; - /* Get the current time (1-60 seconds) */ - dev->kbd_time = out[2] >> HCI_MISC_SHIFT; - /* Flag as supported */ - dev->kbd_illum_supported = 1; + return; } + + if (out[0] != TOS_SUCCESS) + return; + + /* + * Check for keyboard backlight timeout max value, + * previous kbd backlight implementation set this to + * 0x3c0003, and now the new implementation set this + * to 0x3c001a, use this to distinguish between them. + */ + if (out[3] == SCI_KBD_TIME_MAX) + dev->kbd_type = 2; + else + dev->kbd_type = 1; + /* Get the current keyboard backlight mode */ + dev->kbd_mode = out[2] & SCI_KBD_MODE_MASK; + /* Get the current time (1-60 seconds) */ + dev->kbd_time = out[2] >> HCI_MISC_SHIFT; + /* Flag as supported */ + dev->kbd_illum_supported = 1; } static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time) @@ -672,9 +679,9 @@ static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state) /* Eco Mode support */ static void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev) { - acpi_status status; u32 in[TCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 0, 0, 0 }; u32 out[TCI_WORDS]; + acpi_status status; dev->eco_supported = 0; dev->eco_led_registered = false; @@ -682,7 +689,10 @@ static void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev) status = tci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_err("ACPI call to get ECO led failed\n"); - } else if (out[0] == TOS_INPUT_DATA_ERROR) { + return; + } + + if (out[0] == TOS_INPUT_DATA_ERROR) { /* * If we receive 0x8300 (Input Data Error), it means that the * LED device is present, but that we just screwed the input @@ -694,10 +704,15 @@ static void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev) */ in[3] = 1; status = tci_raw(dev, in, out); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { pr_err("ACPI call to get ECO led failed\n"); - else if (out[0] == TOS_SUCCESS) - dev->eco_supported = 1; + return; + } + + if (out[0] != TOS_SUCCESS) + return; + + dev->eco_supported = 1; } } @@ -714,10 +729,11 @@ toshiba_eco_mode_get_status(struct led_classdev *cdev) if (ACPI_FAILURE(status)) { pr_err("ACPI call to get ECO led failed\n"); return LED_OFF; - } else if (out[0] != TOS_SUCCESS) { - return LED_OFF; } + if (out[0] != TOS_SUCCESS) + return LED_OFF; + return out[2] ? LED_FULL : LED_OFF; } @@ -751,10 +767,15 @@ static void toshiba_accelerometer_available(struct toshiba_acpi_dev *dev) * this call also serves as initialization */ status = tci_raw(dev, in, out); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { pr_err("ACPI call to query the accelerometer failed\n"); - else if (out[0] == TOS_SUCCESS) - dev->accelerometer_supported = 1; + return; + } + + if (out[0] != TOS_SUCCESS) + return; + + dev->accelerometer_supported = 1; } static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev, @@ -769,15 +790,18 @@ static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev, if (ACPI_FAILURE(status)) { pr_err("ACPI call to query the accelerometer failed\n"); return -EIO; - } else if (out[0] == TOS_NOT_SUPPORTED) { - return -ENODEV; - } else if (out[0] == TOS_SUCCESS) { - *xy = out[2]; - *z = out[4]; - return 0; } - return -EIO; + if (out[0] == TOS_NOT_SUPPORTED) + return -ENODEV; + + if (out[0] != TOS_SUCCESS) + return -EIO; + + *xy = out[2]; + *z = out[4]; + + return 0; } /* Sleep (Charge and Music) utilities support */ @@ -797,24 +821,29 @@ static void toshiba_usb_sleep_charge_available(struct toshiba_acpi_dev *dev) pr_err("ACPI call to get USB Sleep and Charge mode failed\n"); sci_close(dev); return; - } else if (out[0] == TOS_NOT_SUPPORTED) { + } + + if (out[0] != TOS_SUCCESS) { sci_close(dev); return; - } else if (out[0] == TOS_SUCCESS) { - dev->usbsc_mode_base = out[4]; } + dev->usbsc_mode_base = out[4]; + in[5] = SCI_USB_CHARGE_BAT_LVL; status = tci_raw(dev, in, out); sci_close(dev); if (ACPI_FAILURE(status)) { pr_err("ACPI call to get USB Sleep and Charge mode failed\n"); - } else if (out[0] == TOS_SUCCESS) { - dev->usbsc_bat_level = out[2]; - /* Flag as supported */ - dev->usb_sleep_charge_supported = 1; + return; } + if (out[0] != TOS_SUCCESS) + return; + + dev->usbsc_bat_level = out[2]; + /* Flag as supported */ + dev->usb_sleep_charge_supported = 1; } static int toshiba_usb_sleep_charge_get(struct toshiba_acpi_dev *dev, @@ -868,14 +897,19 @@ static int toshiba_sleep_functions_status_get(struct toshiba_acpi_dev *dev, sci_close(dev); if (ACPI_FAILURE(status)) { pr_err("ACPI call to get USB S&C battery level failed\n"); - } else if (out[0] == TOS_NOT_SUPPORTED) { - return -ENODEV; - } else if (out[0] == TOS_SUCCESS) { - *mode = out[2]; - return 0; + return -EIO; } - return -EIO; + if (out[0] == TOS_NOT_SUPPORTED) + return -ENODEV; + + if (out[0] != TOS_SUCCESS) + return -EIO; + + *mode = out[2]; + + return 0; + } static int toshiba_sleep_functions_status_set(struct toshiba_acpi_dev *dev, @@ -892,9 +926,12 @@ static int toshiba_sleep_functions_status_set(struct toshiba_acpi_dev *dev, in[5] = SCI_USB_CHARGE_BAT_LVL; status = tci_raw(dev, in, out); sci_close(dev); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { pr_err("ACPI call to set USB S&C battery level failed\n"); - else if (out[0] == TOS_NOT_SUPPORTED) + return -EIO; + } + + if (out[0] == TOS_NOT_SUPPORTED) return -ENODEV; return out[0] == TOS_SUCCESS ? 0 : -EIO; @@ -915,14 +952,18 @@ static int toshiba_usb_rapid_charge_get(struct toshiba_acpi_dev *dev, sci_close(dev); if (ACPI_FAILURE(status)) { pr_err("ACPI call to get USB Rapid Charge failed\n"); - } else if (out[0] == TOS_NOT_SUPPORTED) { - return -ENODEV; - } else if (out[0] == TOS_SUCCESS || out[0] == TOS_SUCCESS2) { - *state = out[2]; - return 0; + return -EIO; } - return -EIO; + if (out[0] == TOS_NOT_SUPPORTED) + return -ENODEV; + + if (out[0] != TOS_SUCCESS && out[0] != TOS_SUCCESS2) + return -EIO; + + *state = out[2]; + + return 0; } static int toshiba_usb_rapid_charge_set(struct toshiba_acpi_dev *dev, @@ -939,9 +980,12 @@ static int toshiba_usb_rapid_charge_set(struct toshiba_acpi_dev *dev, in[5] = SCI_USB_CHARGE_RAPID_DSP; status = tci_raw(dev, in, out); sci_close(dev); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { pr_err("ACPI call to set USB Rapid Charge failed\n"); - else if (out[0] == TOS_NOT_SUPPORTED) + return -EIO; + } + + if (out[0] == TOS_NOT_SUPPORTED) return -ENODEV; return (out[0] == TOS_SUCCESS || out[0] == TOS_SUCCESS2) ? 0 : -EIO; @@ -1097,14 +1141,18 @@ static int toshiba_hotkey_event_type_get(struct toshiba_acpi_dev *dev, status = tci_raw(dev, in, out); if (ACPI_FAILURE(status)) { pr_err("ACPI call to get System type failed\n"); - } else if (out[0] == TOS_NOT_SUPPORTED) { - return -ENODEV; - } else if (out[0] == TOS_SUCCESS) { - *type = out[3]; - return 0; + return -EIO; } - return -EIO; + if (out[0] == TOS_NOT_SUPPORTED) + return -ENODEV; + + if (out[0] != TOS_SUCCESS) + return -EIO; + + *type = out[3]; + + return 0; } /* Wireless status (RFKill, WLAN, BT, WWAN) */ @@ -1154,7 +1202,6 @@ static void toshiba_wwan_available(struct toshiba_acpi_dev *dev) */ in[3] = HCI_WIRELESS_WWAN; status = tci_raw(dev, in, out); - if (ACPI_FAILURE(status)) { pr_err("ACPI call to get WWAN status failed\n"); return; @@ -1174,7 +1221,6 @@ static int toshiba_wwan_set(struct toshiba_acpi_dev *dev, u32 state) in[3] = HCI_WIRELESS_WWAN_STATUS; status = tci_raw(dev, in, out); - if (ACPI_FAILURE(status)) { pr_err("ACPI call to set WWAN status failed\n"); return -EIO; @@ -1193,7 +1239,6 @@ static int toshiba_wwan_set(struct toshiba_acpi_dev *dev, u32 state) */ in[3] = HCI_WIRELESS_WWAN_POWER; status = tci_raw(dev, in, out); - if (ACPI_FAILURE(status)) { pr_err("ACPI call to set WWAN power failed\n"); return -EIO; @@ -1216,8 +1261,10 @@ static void toshiba_cooling_method_available(struct toshiba_acpi_dev *dev) dev->max_cooling_method = 0; status = tci_raw(dev, in, out); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { pr_err("ACPI call to get Cooling Method failed\n"); + return; + } if (out[0] != TOS_SUCCESS && out[0] != TOS_SUCCESS2) return; @@ -1244,7 +1291,7 @@ static int toshiba_cooling_method_set(struct toshiba_acpi_dev *dev, u32 state) u32 result = hci_write(dev, HCI_COOLING_METHOD, state); if (result == TOS_FAILURE) - pr_err("ACPI call to get Cooling Method failed\n"); + pr_err("ACPI call to set Cooling Method failed\n"); if (result == TOS_NOT_SUPPORTED) return -ENODEV; @@ -1282,9 +1329,9 @@ static struct proc_dir_entry *toshiba_proc_dir; /* LCD Brightness */ static int __get_lcd_brightness(struct toshiba_acpi_dev *dev) { + int brightness = 0; u32 result; u32 value; - int brightness = 0; if (dev->tr_backlight_supported) { int ret = get_tr_backlight_status(dev, &value); @@ -1301,10 +1348,10 @@ static int __get_lcd_brightness(struct toshiba_acpi_dev *dev) pr_err("ACPI call to get LCD Brightness failed\n"); else if (result == TOS_NOT_SUPPORTED) return -ENODEV; - if (result == TOS_SUCCESS) - return brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT); - return -EIO; + return result == TOS_SUCCESS ? + brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT) : + -EIO; } static int get_lcd_brightness(struct backlight_device *bd) @@ -1325,15 +1372,15 @@ static int lcd_proc_show(struct seq_file *m, void *v) levels = dev->backlight_dev->props.max_brightness + 1; value = get_lcd_brightness(dev->backlight_dev); - if (value >= 0) { - seq_printf(m, "brightness: %d\n", value); - seq_printf(m, "brightness_levels: %d\n", levels); - return 0; + if (value < 0) { + pr_err("Error reading LCD brightness\n"); + return value; } - pr_err("Error reading LCD brightness\n"); + seq_printf(m, "brightness: %d\n", value); + seq_printf(m, "brightness_levels: %d\n", levels); - return -EIO; + return 0; } static int lcd_proc_open(struct inode *inode, struct file *file) @@ -1377,7 +1424,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf, struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file)); char cmd[42]; size_t len; - int levels = dev->backlight_dev->props.max_brightness + 1; + int levels; int value; len = min(count, sizeof(cmd) - 1); @@ -1385,6 +1432,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf, return -EFAULT; cmd[len] = '\0'; + levels = dev->backlight_dev->props.max_brightness + 1; if (sscanf(cmd, " brightness : %i", &value) != 1 && value < 0 && value > levels) return -EINVAL; @@ -1420,20 +1468,21 @@ static int get_video_status(struct toshiba_acpi_dev *dev, u32 *status) static int video_proc_show(struct seq_file *m, void *v) { struct toshiba_acpi_dev *dev = m->private; + int is_lcd, is_crt, is_tv; u32 value; - if (!get_video_status(dev, &value)) { - int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; - int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; - int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0; + if (get_video_status(dev, &value)) + return -EIO; - seq_printf(m, "lcd_out: %d\n", is_lcd); - seq_printf(m, "crt_out: %d\n", is_crt); - seq_printf(m, "tv_out: %d\n", is_tv); - return 0; - } + is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; + is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; + is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0; - return -EIO; + seq_printf(m, "lcd_out: %d\n", is_lcd); + seq_printf(m, "crt_out: %d\n", is_crt); + seq_printf(m, "tv_out: %d\n", is_tv); + + return 0; } static int video_proc_open(struct inode *inode, struct file *file) @@ -1447,10 +1496,8 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf, struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file)); char *buffer; char *cmd; + int lcd_out, crt_out, tv_out; int remain = count; - int lcd_out = -1; - int crt_out = -1; - int tv_out = -1; int value; int ret; u32 video_out; @@ -1486,6 +1533,7 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf, kfree(cmd); + lcd_out = crt_out = tv_out = -1; ret = get_video_status(dev, &video_out); if (!ret) { unsigned int new_video_out = video_out; @@ -1980,8 +2028,8 @@ static ssize_t usb_sleep_charge_store(struct device *dev, const char *buf, size_t count) { struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); - u32 mode; int state; + u32 mode; int ret; ret = kstrtoint(buf, 0, &state); @@ -2021,9 +2069,8 @@ static ssize_t sleep_functions_on_battery_show(struct device *dev, char *buf) { struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + int bat_lvl, status; u32 state; - int bat_lvl; - int status; int ret; int tmp; diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index 5db495dd018e..be1d137c6079 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -80,7 +80,9 @@ static int toshiba_bluetooth_present(acpi_handle handle) if (ACPI_FAILURE(result)) { pr_err("ACPI call to query Bluetooth presence failed\n"); return -ENXIO; - } else if (!bt_present) { + } + + if (!bt_present) { pr_info("Bluetooth device not present\n"); return -ENODEV; } diff --git a/drivers/platform/x86/toshiba_haps.c b/drivers/platform/x86/toshiba_haps.c index 7f2afc6b5eb9..b3dec521e2b6 100644 --- a/drivers/platform/x86/toshiba_haps.c +++ b/drivers/platform/x86/toshiba_haps.c @@ -59,7 +59,7 @@ static int toshiba_haps_protection_level(acpi_handle handle, int level) return -EIO; } - pr_info("HDD protection level set to: %d\n", level); + pr_debug("HDD protection level set to: %d\n", level); return 0; } @@ -141,7 +141,7 @@ static struct attribute_group haps_attr_group = { */ static void toshiba_haps_notify(struct acpi_device *device, u32 event) { - pr_info("Received event: 0x%x", event); + pr_debug("Received event: 0x%x", event); acpi_bus_generate_netlink_event(device->pnp.device_class, dev_name(&device->dev), @@ -168,9 +168,13 @@ static int toshiba_haps_available(acpi_handle handle) * A non existent device as well as having (only) * Solid State Drives can cause the call to fail. */ - status = acpi_evaluate_integer(handle, "_STA", NULL, - &hdd_present); - if (ACPI_FAILURE(status) || !hdd_present) { + status = acpi_evaluate_integer(handle, "_STA", NULL, &hdd_present); + if (ACPI_FAILURE(status)) { + pr_err("ACPI call to query HDD protection failed\n"); + return 0; + } + + if (!hdd_present) { pr_info("HDD protection not available or using SSD\n"); return 0; } diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c index d637c933c8a9..58a97d420572 100644 --- a/drivers/ptp/ptp_chardev.c +++ b/drivers/ptp/ptp_chardev.c @@ -193,6 +193,7 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) if (err) break; + memset(&precise_offset, 0, sizeof(precise_offset)); ts = ktime_to_timespec64(xtstamp.device); precise_offset.device.sec = ts.tv_sec; precise_offset.device.nsec = ts.tv_nsec; diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 80a566a00d04..bf0128899c09 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -262,6 +262,15 @@ config PWM_LPSS_PLATFORM To compile this driver as a module, choose M here: the module will be called pwm-lpss-platform. +config PWM_MESON + tristate "Amlogic Meson PWM driver" + depends on ARCH_MESON + help + The platform driver for Amlogic Meson PWM controller. + + To compile this driver as a module, choose M here: the module + will be called pwm-meson. + config PWM_MTK_DISP tristate "MediaTek display PWM driver" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index feef1dd29f73..1194c54efcc2 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o +obj-$(CONFIG_PWM_MESON) += pwm-meson.o obj-$(CONFIG_PWM_MTK_DISP) += pwm-mtk-disp.o obj-$(CONFIG_PWM_MXS) += pwm-mxs.o obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 0dbd29e287db..172ef8245811 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -339,6 +339,8 @@ int pwmchip_remove(struct pwm_chip *chip) unsigned int i; int ret = 0; + pwmchip_sysfs_unexport_children(chip); + mutex_lock(&pwm_lock); for (i = 0; i < chip->npwm; i++) { diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c index 65108129d505..01339c152ab0 100644 --- a/drivers/pwm/pwm-berlin.c +++ b/drivers/pwm/pwm-berlin.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pwm.h> +#include <linux/slab.h> #define BERLIN_PWM_EN 0x0 #define BERLIN_PWM_ENABLE BIT(0) @@ -27,6 +28,13 @@ #define BERLIN_PWM_TCNT 0xc #define BERLIN_PWM_MAX_TCNT 65535 +struct berlin_pwm_channel { + u32 enable; + u32 ctrl; + u32 duty; + u32 tcnt; +}; + struct berlin_pwm_chip { struct pwm_chip chip; struct clk *clk; @@ -55,6 +63,25 @@ static inline void berlin_pwm_writel(struct berlin_pwm_chip *chip, writel_relaxed(value, chip->base + channel * 0x10 + offset); } +static int berlin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct berlin_pwm_channel *channel; + + channel = kzalloc(sizeof(*channel), GFP_KERNEL); + if (!channel) + return -ENOMEM; + + return pwm_set_chip_data(pwm, channel); +} + +static void berlin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct berlin_pwm_channel *channel = pwm_get_chip_data(pwm); + + pwm_set_chip_data(pwm, NULL); + kfree(channel); +} + static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev, int duty_ns, int period_ns) { @@ -137,6 +164,8 @@ static void berlin_pwm_disable(struct pwm_chip *chip, } static const struct pwm_ops berlin_pwm_ops = { + .request = berlin_pwm_request, + .free = berlin_pwm_free, .config = berlin_pwm_config, .set_polarity = berlin_pwm_set_polarity, .enable = berlin_pwm_enable, @@ -204,12 +233,67 @@ static int berlin_pwm_remove(struct platform_device *pdev) return ret; } +#ifdef CONFIG_PM_SLEEP +static int berlin_pwm_suspend(struct device *dev) +{ + struct berlin_pwm_chip *pwm = dev_get_drvdata(dev); + unsigned int i; + + for (i = 0; i < pwm->chip.npwm; i++) { + struct berlin_pwm_channel *channel; + + channel = pwm_get_chip_data(&pwm->chip.pwms[i]); + if (!channel) + continue; + + channel->enable = berlin_pwm_readl(pwm, i, BERLIN_PWM_ENABLE); + channel->ctrl = berlin_pwm_readl(pwm, i, BERLIN_PWM_CONTROL); + channel->duty = berlin_pwm_readl(pwm, i, BERLIN_PWM_DUTY); + channel->tcnt = berlin_pwm_readl(pwm, i, BERLIN_PWM_TCNT); + } + + clk_disable_unprepare(pwm->clk); + + return 0; +} + +static int berlin_pwm_resume(struct device *dev) +{ + struct berlin_pwm_chip *pwm = dev_get_drvdata(dev); + unsigned int i; + int ret; + + ret = clk_prepare_enable(pwm->clk); + if (ret) + return ret; + + for (i = 0; i < pwm->chip.npwm; i++) { + struct berlin_pwm_channel *channel; + + channel = pwm_get_chip_data(&pwm->chip.pwms[i]); + if (!channel) + continue; + + berlin_pwm_writel(pwm, i, channel->ctrl, BERLIN_PWM_CONTROL); + berlin_pwm_writel(pwm, i, channel->duty, BERLIN_PWM_DUTY); + berlin_pwm_writel(pwm, i, channel->tcnt, BERLIN_PWM_TCNT); + berlin_pwm_writel(pwm, i, channel->enable, BERLIN_PWM_ENABLE); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(berlin_pwm_pm_ops, berlin_pwm_suspend, + berlin_pwm_resume); + static struct platform_driver berlin_pwm_driver = { .probe = berlin_pwm_probe, .remove = berlin_pwm_remove, .driver = { .name = "berlin-pwm", .of_match_table = berlin_pwm_match, + .pm = &berlin_pwm_pm_ops, }, }; module_platform_driver(berlin_pwm_driver); diff --git a/drivers/pwm/pwm-cros-ec.c b/drivers/pwm/pwm-cros-ec.c index 99b9acc1a420..f6ca4e8c6253 100644 --- a/drivers/pwm/pwm-cros-ec.c +++ b/drivers/pwm/pwm-cros-ec.c @@ -38,7 +38,7 @@ static int cros_ec_pwm_set_duty(struct cros_ec_device *ec, u8 index, u16 duty) struct { struct cros_ec_command msg; struct ec_params_pwm_set_duty params; - } buf; + } __packed buf; struct ec_params_pwm_set_duty *params = &buf.params; struct cros_ec_command *msg = &buf.msg; @@ -65,7 +65,7 @@ static int __cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index, struct ec_params_pwm_get_duty params; struct ec_response_pwm_get_duty resp; }; - } buf; + } __packed buf; struct ec_params_pwm_get_duty *params = &buf.params; struct ec_response_pwm_get_duty *resp = &buf.resp; struct cros_ec_command *msg = &buf.msg; diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c index 19dc64cab2f0..d7f5f7de030d 100644 --- a/drivers/pwm/pwm-lpc18xx-sct.c +++ b/drivers/pwm/pwm-lpc18xx-sct.c @@ -413,14 +413,18 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev) } for (i = 0; i < lpc18xx_pwm->chip.npwm; i++) { + struct lpc18xx_pwm_data *data; + pwm = &lpc18xx_pwm->chip.pwms[i]; - pwm->chip_data = devm_kzalloc(lpc18xx_pwm->dev, - sizeof(struct lpc18xx_pwm_data), - GFP_KERNEL); - if (!pwm->chip_data) { + + data = devm_kzalloc(lpc18xx_pwm->dev, sizeof(*data), + GFP_KERNEL); + if (!data) { ret = -ENOMEM; goto remove_pwmchip; } + + pwm_set_chip_data(pwm, data); } platform_set_drvdata(pdev, lpc18xx_pwm); diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c new file mode 100644 index 000000000000..381871b2bb46 --- /dev/null +++ b/drivers/pwm/pwm-meson.c @@ -0,0 +1,529 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright (c) 2016 BayLibre, SAS. + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Amlogic, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * BSD LICENSE + * + * Copyright (c) 2016 BayLibre, SAS. + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Amlogic, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#define REG_PWM_A 0x0 +#define REG_PWM_B 0x4 +#define PWM_HIGH_SHIFT 16 + +#define REG_MISC_AB 0x8 +#define MISC_B_CLK_EN BIT(23) +#define MISC_A_CLK_EN BIT(15) +#define MISC_CLK_DIV_MASK 0x7f +#define MISC_B_CLK_DIV_SHIFT 16 +#define MISC_A_CLK_DIV_SHIFT 8 +#define MISC_B_CLK_SEL_SHIFT 6 +#define MISC_A_CLK_SEL_SHIFT 4 +#define MISC_CLK_SEL_WIDTH 2 +#define MISC_B_EN BIT(1) +#define MISC_A_EN BIT(0) + +static const unsigned int mux_reg_shifts[] = { + MISC_A_CLK_SEL_SHIFT, + MISC_B_CLK_SEL_SHIFT +}; + +struct meson_pwm_channel { + unsigned int hi; + unsigned int lo; + u8 pre_div; + + struct pwm_state state; + + struct clk *clk_parent; + struct clk_mux mux; + struct clk *clk; +}; + +struct meson_pwm_data { + const char * const *parent_names; +}; + +struct meson_pwm { + struct pwm_chip chip; + const struct meson_pwm_data *data; + void __iomem *base; + u8 inverter_mask; + spinlock_t lock; +}; + +static inline struct meson_pwm *to_meson_pwm(struct pwm_chip *chip) +{ + return container_of(chip, struct meson_pwm, chip); +} + +static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); + struct device *dev = chip->dev; + int err; + + if (!channel) + return -ENODEV; + + if (channel->clk_parent) { + err = clk_set_parent(channel->clk, channel->clk_parent); + if (err < 0) { + dev_err(dev, "failed to set parent %s for %s: %d\n", + __clk_get_name(channel->clk_parent), + __clk_get_name(channel->clk), err); + return err; + } + } + + err = clk_prepare_enable(channel->clk); + if (err < 0) { + dev_err(dev, "failed to enable clock %s: %d\n", + __clk_get_name(channel->clk), err); + return err; + } + + chip->ops->get_state(chip, pwm, &channel->state); + + return 0; +} + +static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); + + if (channel) + clk_disable_unprepare(channel->clk); +} + +static int meson_pwm_calc(struct meson_pwm *meson, + struct meson_pwm_channel *channel, unsigned int id, + unsigned int duty, unsigned int period) +{ + unsigned int pre_div, cnt, duty_cnt; + unsigned long fin_freq = -1, fin_ns; + + if (~(meson->inverter_mask >> id) & 0x1) + duty = period - duty; + + if (period == channel->state.period && + duty == channel->state.duty_cycle) + return 0; + + fin_freq = clk_get_rate(channel->clk); + if (fin_freq == 0) { + dev_err(meson->chip.dev, "invalid source clock frequency\n"); + return -EINVAL; + } + + dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq); + fin_ns = NSEC_PER_SEC / fin_freq; + + /* Calc pre_div with the period */ + for (pre_div = 0; pre_div < MISC_CLK_DIV_MASK; pre_div++) { + cnt = DIV_ROUND_CLOSEST(period, fin_ns * (pre_div + 1)); + dev_dbg(meson->chip.dev, "fin_ns=%lu pre_div=%u cnt=%u\n", + fin_ns, pre_div, cnt); + if (cnt <= 0xffff) + break; + } + + if (pre_div == MISC_CLK_DIV_MASK) { + dev_err(meson->chip.dev, "unable to get period pre_div\n"); + return -EINVAL; + } + + dev_dbg(meson->chip.dev, "period=%u pre_div=%u cnt=%u\n", period, + pre_div, cnt); + + if (duty == period) { + channel->pre_div = pre_div; + channel->hi = cnt; + channel->lo = 0; + } else if (duty == 0) { + channel->pre_div = pre_div; + channel->hi = 0; + channel->lo = cnt; + } else { + /* Then check is we can have the duty with the same pre_div */ + duty_cnt = DIV_ROUND_CLOSEST(duty, fin_ns * (pre_div + 1)); + if (duty_cnt > 0xffff) { + dev_err(meson->chip.dev, "unable to get duty cycle\n"); + return -EINVAL; + } + + dev_dbg(meson->chip.dev, "duty=%u pre_div=%u duty_cnt=%u\n", + duty, pre_div, duty_cnt); + + channel->pre_div = pre_div; + channel->hi = duty_cnt; + channel->lo = cnt - duty_cnt; + } + + return 0; +} + +static void meson_pwm_enable(struct meson_pwm *meson, + struct meson_pwm_channel *channel, + unsigned int id) +{ + u32 value, clk_shift, clk_enable, enable; + unsigned int offset; + + switch (id) { + case 0: + clk_shift = MISC_A_CLK_DIV_SHIFT; + clk_enable = MISC_A_CLK_EN; + enable = MISC_A_EN; + offset = REG_PWM_A; + break; + + case 1: + clk_shift = MISC_B_CLK_DIV_SHIFT; + clk_enable = MISC_B_CLK_EN; + enable = MISC_B_EN; + offset = REG_PWM_B; + break; + + default: + return; + } + + value = readl(meson->base + REG_MISC_AB); + value &= ~(MISC_CLK_DIV_MASK << clk_shift); + value |= channel->pre_div << clk_shift; + value |= clk_enable; + writel(value, meson->base + REG_MISC_AB); + + value = (channel->hi << PWM_HIGH_SHIFT) | channel->lo; + writel(value, meson->base + offset); + + value = readl(meson->base + REG_MISC_AB); + value |= enable; + writel(value, meson->base + REG_MISC_AB); +} + +static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id) +{ + u32 value, enable; + + switch (id) { + case 0: + enable = MISC_A_EN; + break; + + case 1: + enable = MISC_B_EN; + break; + + default: + return; + } + + value = readl(meson->base + REG_MISC_AB); + value &= ~enable; + writel(value, meson->base + REG_MISC_AB); +} + +static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct meson_pwm_channel *channel = pwm_get_chip_data(pwm); + struct meson_pwm *meson = to_meson_pwm(chip); + unsigned long flags; + int err = 0; + + if (!state) + return -EINVAL; + + spin_lock_irqsave(&meson->lock, flags); + + if (!state->enabled) { + meson_pwm_disable(meson, pwm->hwpwm); + channel->state.enabled = false; + + goto unlock; + } + + if (state->period != channel->state.period || + state->duty_cycle != channel->state.duty_cycle || + state->polarity != channel->state.polarity) { + if (channel->state.enabled) { + meson_pwm_disable(meson, pwm->hwpwm); + channel->state.enabled = false; + } + + if (state->polarity != channel->state.polarity) { + if (state->polarity == PWM_POLARITY_NORMAL) + meson->inverter_mask |= BIT(pwm->hwpwm); + else + meson->inverter_mask &= ~BIT(pwm->hwpwm); + } + + err = meson_pwm_calc(meson, channel, pwm->hwpwm, + state->duty_cycle, state->period); + if (err < 0) + goto unlock; + + channel->state.polarity = state->polarity; + channel->state.period = state->period; + channel->state.duty_cycle = state->duty_cycle; + } + + if (state->enabled && !channel->state.enabled) { + meson_pwm_enable(meson, channel, pwm->hwpwm); + channel->state.enabled = true; + } + +unlock: + spin_unlock_irqrestore(&meson->lock, flags); + return err; +} + +static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct meson_pwm *meson = to_meson_pwm(chip); + u32 value, mask; + + if (!state) + return; + + switch (pwm->hwpwm) { + case 0: + mask = MISC_A_EN; + break; + + case 1: + mask = MISC_B_EN; + break; + + default: + return; + } + + value = readl(meson->base + REG_MISC_AB); + state->enabled = (value & mask) != 0; +} + +static const struct pwm_ops meson_pwm_ops = { + .request = meson_pwm_request, + .free = meson_pwm_free, + .apply = meson_pwm_apply, + .get_state = meson_pwm_get_state, + .owner = THIS_MODULE, +}; + +static const char * const pwm_meson8b_parent_names[] = { + "xtal", "vid_pll", "fclk_div4", "fclk_div3" +}; + +static const struct meson_pwm_data pwm_meson8b_data = { + .parent_names = pwm_meson8b_parent_names, +}; + +static const char * const pwm_gxbb_parent_names[] = { + "xtal", "hdmi_pll", "fclk_div4", "fclk_div3" +}; + +static const struct meson_pwm_data pwm_gxbb_data = { + .parent_names = pwm_gxbb_parent_names, +}; + +static const struct of_device_id meson_pwm_matches[] = { + { .compatible = "amlogic,meson8b-pwm", .data = &pwm_meson8b_data }, + { .compatible = "amlogic,meson-gxbb-pwm", .data = &pwm_gxbb_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, meson_pwm_matches); + +static int meson_pwm_init_channels(struct meson_pwm *meson, + struct meson_pwm_channel *channels) +{ + struct device *dev = meson->chip.dev; + struct device_node *np = dev->of_node; + struct clk_init_data init; + unsigned int i; + char name[255]; + int err; + + for (i = 0; i < meson->chip.npwm; i++) { + struct meson_pwm_channel *channel = &channels[i]; + + snprintf(name, sizeof(name), "%s#mux%u", np->full_name, i); + + init.name = name; + init.ops = &clk_mux_ops; + init.flags = CLK_IS_BASIC; + init.parent_names = meson->data->parent_names; + init.num_parents = 1 << MISC_CLK_SEL_WIDTH; + + channel->mux.reg = meson->base + REG_MISC_AB; + channel->mux.shift = mux_reg_shifts[i]; + channel->mux.mask = BIT(MISC_CLK_SEL_WIDTH) - 1; + channel->mux.flags = 0; + channel->mux.lock = &meson->lock; + channel->mux.table = NULL; + channel->mux.hw.init = &init; + + channel->clk = devm_clk_register(dev, &channel->mux.hw); + if (IS_ERR(channel->clk)) { + err = PTR_ERR(channel->clk); + dev_err(dev, "failed to register %s: %d\n", name, err); + return err; + } + + snprintf(name, sizeof(name), "clkin%u", i); + + channel->clk_parent = devm_clk_get(dev, name); + if (IS_ERR(channel->clk_parent)) { + err = PTR_ERR(channel->clk_parent); + if (err == -EPROBE_DEFER) + return err; + + channel->clk_parent = NULL; + } + } + + return 0; +} + +static void meson_pwm_add_channels(struct meson_pwm *meson, + struct meson_pwm_channel *channels) +{ + unsigned int i; + + for (i = 0; i < meson->chip.npwm; i++) + pwm_set_chip_data(&meson->chip.pwms[i], &channels[i]); +} + +static int meson_pwm_probe(struct platform_device *pdev) +{ + struct meson_pwm_channel *channels; + struct meson_pwm *meson; + struct resource *regs; + int err; + + meson = devm_kzalloc(&pdev->dev, sizeof(*meson), GFP_KERNEL); + if (!meson) + return -ENOMEM; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + meson->base = devm_ioremap_resource(&pdev->dev, regs); + if (IS_ERR(meson->base)) + return PTR_ERR(meson->base); + + meson->chip.dev = &pdev->dev; + meson->chip.ops = &meson_pwm_ops; + meson->chip.base = -1; + meson->chip.npwm = 2; + meson->chip.of_xlate = of_pwm_xlate_with_flags; + meson->chip.of_pwm_n_cells = 3; + + meson->data = of_device_get_match_data(&pdev->dev); + meson->inverter_mask = BIT(meson->chip.npwm) - 1; + + channels = devm_kcalloc(&pdev->dev, meson->chip.npwm, sizeof(*meson), + GFP_KERNEL); + if (!channels) + return -ENOMEM; + + err = meson_pwm_init_channels(meson, channels); + if (err < 0) + return err; + + err = pwmchip_add(&meson->chip); + if (err < 0) { + dev_err(&pdev->dev, "failed to register PWM chip: %d\n", err); + return err; + } + + meson_pwm_add_channels(meson, channels); + + platform_set_drvdata(pdev, meson); + + return 0; +} + +static int meson_pwm_remove(struct platform_device *pdev) +{ + struct meson_pwm *meson = platform_get_drvdata(pdev); + + return pwmchip_remove(&meson->chip); +} + +static struct platform_driver meson_pwm_driver = { + .driver = { + .name = "meson-pwm", + .of_match_table = meson_pwm_matches, + }, + .probe = meson_pwm_probe, + .remove = meson_pwm_remove, +}; +module_platform_driver(meson_pwm_driver); + +MODULE_ALIAS("platform:meson-pwm"); +MODULE_DESCRIPTION("Amlogic Meson PWM Generator driver"); +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c index 0ad3385298c0..893940d45f0d 100644 --- a/drivers/pwm/pwm-mtk-disp.c +++ b/drivers/pwm/pwm-mtk-disp.c @@ -18,30 +18,40 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> #define DISP_PWM_EN 0x00 -#define PWM_ENABLE_MASK BIT(0) -#define DISP_PWM_COMMIT 0x08 -#define PWM_COMMIT_MASK BIT(0) - -#define DISP_PWM_CON_0 0x10 #define PWM_CLKDIV_SHIFT 16 #define PWM_CLKDIV_MAX 0x3ff #define PWM_CLKDIV_MASK (PWM_CLKDIV_MAX << PWM_CLKDIV_SHIFT) -#define DISP_PWM_CON_1 0x14 #define PWM_PERIOD_BIT_WIDTH 12 #define PWM_PERIOD_MASK ((1 << PWM_PERIOD_BIT_WIDTH) - 1) #define PWM_HIGH_WIDTH_SHIFT 16 #define PWM_HIGH_WIDTH_MASK (0x1fff << PWM_HIGH_WIDTH_SHIFT) +struct mtk_pwm_data { + u32 enable_mask; + unsigned int con0; + u32 con0_sel; + unsigned int con1; + + bool has_commit; + unsigned int commit; + unsigned int commit_mask; + + unsigned int bls_debug; + u32 bls_debug_mask; +}; + struct mtk_disp_pwm { struct pwm_chip chip; + const struct mtk_pwm_data *data; struct clk *clk_main; struct clk *clk_mm; void __iomem *base; @@ -106,12 +116,21 @@ static int mtk_disp_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, return err; } - mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_0, PWM_CLKDIV_MASK, + mtk_disp_pwm_update_bits(mdp, mdp->data->con0, + PWM_CLKDIV_MASK, clk_div << PWM_CLKDIV_SHIFT); - mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_1, - PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK, value); - mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 1); - mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 0); + mtk_disp_pwm_update_bits(mdp, mdp->data->con1, + PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK, + value); + + if (mdp->data->has_commit) { + mtk_disp_pwm_update_bits(mdp, mdp->data->commit, + mdp->data->commit_mask, + mdp->data->commit_mask); + mtk_disp_pwm_update_bits(mdp, mdp->data->commit, + mdp->data->commit_mask, + 0x0); + } clk_disable(mdp->clk_mm); clk_disable(mdp->clk_main); @@ -134,7 +153,8 @@ static int mtk_disp_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) return err; } - mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 1); + mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask, + mdp->data->enable_mask); return 0; } @@ -143,7 +163,8 @@ static void mtk_disp_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip); - mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 0); + mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask, + 0x0); clk_disable(mdp->clk_mm); clk_disable(mdp->clk_main); @@ -166,6 +187,8 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev) if (!mdp) return -ENOMEM; + mdp->data = of_device_get_match_data(&pdev->dev); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); mdp->base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(mdp->base)) @@ -200,6 +223,19 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mdp); + /* + * For MT2701, disable double buffer before writing register + * and select manual mode and use PWM_PERIOD/PWM_HIGH_WIDTH. + */ + if (!mdp->data->has_commit) { + mtk_disp_pwm_update_bits(mdp, mdp->data->bls_debug, + mdp->data->bls_debug_mask, + mdp->data->bls_debug_mask); + mtk_disp_pwm_update_bits(mdp, mdp->data->con0, + mdp->data->con0_sel, + mdp->data->con0_sel); + } + return 0; disable_clk_mm: @@ -221,9 +257,30 @@ static int mtk_disp_pwm_remove(struct platform_device *pdev) return ret; } +static const struct mtk_pwm_data mt2701_pwm_data = { + .enable_mask = BIT(16), + .con0 = 0xa8, + .con0_sel = 0x2, + .con1 = 0xac, + .has_commit = false, + .bls_debug = 0xb0, + .bls_debug_mask = 0x3, +}; + +static const struct mtk_pwm_data mt8173_pwm_data = { + .enable_mask = BIT(0), + .con0 = 0x10, + .con0_sel = 0x0, + .con1 = 0x14, + .has_commit = true, + .commit = 0x8, + .commit_mask = 0x1, +}; + static const struct of_device_id mtk_disp_pwm_of_match[] = { - { .compatible = "mediatek,mt8173-disp-pwm" }, - { .compatible = "mediatek,mt6595-disp-pwm" }, + { .compatible = "mediatek,mt2701-disp-pwm", .data = &mt2701_pwm_data}, + { .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data}, + { .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data}, { } }; MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match); diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index ada2d326dc3e..f113cda47032 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -193,9 +193,18 @@ static unsigned long pwm_samsung_calc_tin(struct samsung_pwm_chip *chip, * divider settings and choose the lowest divisor that can generate * frequencies lower than requested. */ - for (div = variant->div_base; div < 4; ++div) - if ((rate >> (variant->bits + div)) < freq) - break; + if (variant->bits < 32) { + /* Only for s3c24xx */ + for (div = variant->div_base; div < 4; ++div) + if ((rate >> (variant->bits + div)) < freq) + break; + } else { + /* + * Other variants have enough counter bits to generate any + * requested rate, so no need to check higher divisors. + */ + div = variant->div_base; + } pwm_samsung_set_divisor(chip, chan, BIT(div)); diff --git a/drivers/pwm/pwm-sti.c b/drivers/pwm/pwm-sti.c index 92abbd56b9f7..dd82dc840af9 100644 --- a/drivers/pwm/pwm-sti.c +++ b/drivers/pwm/pwm-sti.c @@ -1,8 +1,10 @@ /* - * PWM device driver for ST SoCs. - * Author: Ajit Pal Singh <ajitpal.singh@st.com> + * PWM device driver for ST SoCs + * + * Copyright (C) 2013-2016 STMicroelectronics (R&D) Limited * - * Copyright (C) 2013-2014 STMicroelectronics (R&D) Limited + * Author: Ajit Pal Singh <ajitpal.singh@st.com> + * Lee Jones <lee.jones@linaro.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -11,6 +13,7 @@ */ #include <linux/clk.h> +#include <linux/interrupt.h> #include <linux/math64.h> #include <linux/mfd/syscon.h> #include <linux/module.h> @@ -18,43 +21,82 @@ #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/regmap.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/time.h> +#include <linux/wait.h> + +#define PWM_OUT_VAL(x) (0x00 + (4 * (x))) /* Device's Duty Cycle register */ +#define PWM_CPT_VAL(x) (0x10 + (4 * (x))) /* Capture value */ +#define PWM_CPT_EDGE(x) (0x30 + (4 * (x))) /* Edge to capture on */ -#define STI_DS_REG(ch) (4 * (ch)) /* Channel's Duty Cycle register */ -#define STI_PWMCR 0x50 /* Control/Config register */ -#define STI_INTEN 0x54 /* Interrupt Enable/Disable register */ -#define PWM_PRESCALE_LOW_MASK 0x0f -#define PWM_PRESCALE_HIGH_MASK 0xf0 +#define STI_PWM_CTRL 0x50 /* Control/Config register */ +#define STI_INT_EN 0x54 /* Interrupt Enable/Disable register */ +#define STI_INT_STA 0x58 /* Interrupt Status register */ +#define PWM_INT_ACK 0x5c +#define PWM_PRESCALE_LOW_MASK 0x0f +#define PWM_PRESCALE_HIGH_MASK 0xf0 +#define PWM_CPT_EDGE_MASK 0x03 +#define PWM_INT_ACK_MASK 0x1ff + +#define STI_MAX_CPT_DEVS 4 +#define CPT_DC_MAX 0xff /* Regfield IDs */ enum { + /* Bits in PWM_CTRL*/ PWMCLK_PRESCALE_LOW, PWMCLK_PRESCALE_HIGH, - PWM_EN, - PWM_INT_EN, + CPTCLK_PRESCALE, + + PWM_OUT_EN, + PWM_CPT_EN, + + PWM_CPT_INT_EN, + PWM_CPT_INT_STAT, /* Keep last */ MAX_REGFIELDS }; +/* + * Each capture input can be programmed to detect rising-edge, falling-edge, + * either edge or neither egde. + */ +enum sti_cpt_edge { + CPT_EDGE_DISABLED, + CPT_EDGE_RISING, + CPT_EDGE_FALLING, + CPT_EDGE_BOTH, +}; + +struct sti_cpt_ddata { + u32 snapshot[3]; + unsigned int index; + struct mutex lock; + wait_queue_head_t wait; +}; + struct sti_pwm_compat_data { const struct reg_field *reg_fields; - unsigned int num_chan; + unsigned int pwm_num_devs; + unsigned int cpt_num_devs; unsigned int max_pwm_cnt; unsigned int max_prescale; }; struct sti_pwm_chip { struct device *dev; - struct clk *clk; - unsigned long clk_rate; + struct clk *pwm_clk; + struct clk *cpt_clk; struct regmap *regmap; struct sti_pwm_compat_data *cdata; struct regmap_field *prescale_low; struct regmap_field *prescale_high; - struct regmap_field *pwm_en; - struct regmap_field *pwm_int_en; + struct regmap_field *pwm_out_en; + struct regmap_field *pwm_cpt_en; + struct regmap_field *pwm_cpt_int_en; + struct regmap_field *pwm_cpt_int_stat; struct pwm_chip chip; struct pwm_device *cur; unsigned long configured; @@ -64,10 +106,13 @@ struct sti_pwm_chip { }; static const struct reg_field sti_pwm_regfields[MAX_REGFIELDS] = { - [PWMCLK_PRESCALE_LOW] = REG_FIELD(STI_PWMCR, 0, 3), - [PWMCLK_PRESCALE_HIGH] = REG_FIELD(STI_PWMCR, 11, 14), - [PWM_EN] = REG_FIELD(STI_PWMCR, 9, 9), - [PWM_INT_EN] = REG_FIELD(STI_INTEN, 0, 0), + [PWMCLK_PRESCALE_LOW] = REG_FIELD(STI_PWM_CTRL, 0, 3), + [PWMCLK_PRESCALE_HIGH] = REG_FIELD(STI_PWM_CTRL, 11, 14), + [CPTCLK_PRESCALE] = REG_FIELD(STI_PWM_CTRL, 4, 8), + [PWM_OUT_EN] = REG_FIELD(STI_PWM_CTRL, 9, 9), + [PWM_CPT_EN] = REG_FIELD(STI_PWM_CTRL, 10, 10), + [PWM_CPT_INT_EN] = REG_FIELD(STI_INT_EN, 1, 4), + [PWM_CPT_INT_STAT] = REG_FIELD(STI_INT_STA, 1, 4), }; static inline struct sti_pwm_chip *to_sti_pwmchip(struct pwm_chip *chip) @@ -82,61 +127,68 @@ static int sti_pwm_get_prescale(struct sti_pwm_chip *pc, unsigned long period, unsigned int *prescale) { struct sti_pwm_compat_data *cdata = pc->cdata; - unsigned long val; + unsigned long clk_rate; + unsigned long value; unsigned int ps; + clk_rate = clk_get_rate(pc->pwm_clk); + if (!clk_rate) { + dev_err(pc->dev, "failed to get clock rate\n"); + return -EINVAL; + } + /* - * prescale = ((period_ns * clk_rate) / (10^9 * (max_pwm_count + 1)) - 1 + * prescale = ((period_ns * clk_rate) / (10^9 * (max_pwm_cnt + 1)) - 1 */ - val = NSEC_PER_SEC / pc->clk_rate; - val *= cdata->max_pwm_cnt + 1; + value = NSEC_PER_SEC / clk_rate; + value *= cdata->max_pwm_cnt + 1; - if (period % val) { + if (period % value) return -EINVAL; - } else { - ps = period / val - 1; - if (ps > cdata->max_prescale) - return -EINVAL; - } + + ps = period / value - 1; + if (ps > cdata->max_prescale) + return -EINVAL; + *prescale = ps; return 0; } /* - * For STiH4xx PWM IP, the PWM period is fixed to 256 local clock cycles. - * The only way to change the period (apart from changing the PWM input clock) - * is to change the PWM clock prescaler. - * The prescaler is of 8 bits, so 256 prescaler values and hence - * 256 possible period values are supported (for a particular clock rate). - * The requested period will be applied only if it matches one of these - * 256 values. + * For STiH4xx PWM IP, the PWM period is fixed to 256 local clock cycles. The + * only way to change the period (apart from changing the PWM input clock) is + * to change the PWM clock prescaler. + * + * The prescaler is of 8 bits, so 256 prescaler values and hence 256 possible + * period values are supported (for a particular clock rate). The requested + * period will be applied only if it matches one of these 256 values. */ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) + int duty_ns, int period_ns) { struct sti_pwm_chip *pc = to_sti_pwmchip(chip); struct sti_pwm_compat_data *cdata = pc->cdata; + unsigned int ncfg, value, prescale = 0; struct pwm_device *cur = pc->cur; struct device *dev = pc->dev; - unsigned int prescale = 0, pwmvalx; - int ret; - unsigned int ncfg; bool period_same = false; + int ret; ncfg = hweight_long(pc->configured); if (ncfg) period_same = (period_ns == pwm_get_period(cur)); - /* Allow configuration changes if one of the - * following conditions satisfy. - * 1. No channels have been configured. - * 2. Only one channel has been configured and the new request - * is for the same channel. - * 3. Only one channel has been configured and the new request is - * for a new channel and period of the new channel is same as - * the current configured period. - * 4. More than one channels are configured and period of the new + /* + * Allow configuration changes if one of the following conditions + * satisfy. + * 1. No devices have been configured. + * 2. Only one device has been configured and the new request is for + * the same device. + * 3. Only one device has been configured and the new request is for + * a new device and period of the new device is same as the current + * configured period. + * 4. More than one devices are configured and period of the new * requestis the same as the current period. */ if (!ncfg || @@ -144,7 +196,11 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ((ncfg == 1) && (pwm->hwpwm != cur->hwpwm) && period_same) || ((ncfg > 1) && period_same)) { /* Enable clock before writing to PWM registers. */ - ret = clk_enable(pc->clk); + ret = clk_enable(pc->pwm_clk); + if (ret) + return ret; + + ret = clk_enable(pc->cpt_clk); if (ret) return ret; @@ -153,15 +209,15 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, if (ret) goto clk_dis; - ret = - regmap_field_write(pc->prescale_low, - prescale & PWM_PRESCALE_LOW_MASK); + value = prescale & PWM_PRESCALE_LOW_MASK; + + ret = regmap_field_write(pc->prescale_low, value); if (ret) goto clk_dis; - ret = - regmap_field_write(pc->prescale_high, - (prescale & PWM_PRESCALE_HIGH_MASK) >> 4); + value = (prescale & PWM_PRESCALE_HIGH_MASK) >> 4; + + ret = regmap_field_write(pc->prescale_high, value); if (ret) goto clk_dis; } @@ -172,25 +228,26 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * PWM pulse = (max_pwm_count + 1) local cycles, * that is continuous pulse: signal never goes low. */ - pwmvalx = cdata->max_pwm_cnt * duty_ns / period_ns; + value = cdata->max_pwm_cnt * duty_ns / period_ns; - ret = regmap_write(pc->regmap, STI_DS_REG(pwm->hwpwm), pwmvalx); + ret = regmap_write(pc->regmap, PWM_OUT_VAL(pwm->hwpwm), value); if (ret) goto clk_dis; - ret = regmap_field_write(pc->pwm_int_en, 0); + ret = regmap_field_write(pc->pwm_cpt_int_en, 0); set_bit(pwm->hwpwm, &pc->configured); pc->cur = pwm; - dev_dbg(dev, "prescale:%u, period:%i, duty:%i, pwmvalx:%u\n", - prescale, period_ns, duty_ns, pwmvalx); + dev_dbg(dev, "prescale:%u, period:%i, duty:%i, value:%u\n", + prescale, period_ns, duty_ns, value); } else { return -EINVAL; } clk_dis: - clk_disable(pc->clk); + clk_disable(pc->pwm_clk); + clk_disable(pc->cpt_clk); return ret; } @@ -201,23 +258,30 @@ static int sti_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) int ret = 0; /* - * Since we have a common enable for all PWM channels, - * do not enable if already enabled. + * Since we have a common enable for all PWM devices, do not enable if + * already enabled. */ mutex_lock(&pc->sti_pwm_lock); + if (!pc->en_count) { - ret = clk_enable(pc->clk); + ret = clk_enable(pc->pwm_clk); + if (ret) + goto out; + + ret = clk_enable(pc->cpt_clk); if (ret) goto out; - ret = regmap_field_write(pc->pwm_en, 1); + ret = regmap_field_write(pc->pwm_out_en, 1); if (ret) { - dev_err(dev, "failed to enable PWM device:%d\n", - pwm->hwpwm); + dev_err(dev, "failed to enable PWM device %u: %d\n", + pwm->hwpwm, ret); goto out; } } + pc->en_count++; + out: mutex_unlock(&pc->sti_pwm_lock); return ret; @@ -228,13 +292,17 @@ static void sti_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) struct sti_pwm_chip *pc = to_sti_pwmchip(chip); mutex_lock(&pc->sti_pwm_lock); + if (--pc->en_count) { mutex_unlock(&pc->sti_pwm_lock); return; } - regmap_field_write(pc->pwm_en, 0); - clk_disable(pc->clk); + regmap_field_write(pc->pwm_out_en, 0); + + clk_disable(pc->pwm_clk); + clk_disable(pc->cpt_clk); + mutex_unlock(&pc->sti_pwm_lock); } @@ -245,7 +313,90 @@ static void sti_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) clear_bit(pwm->hwpwm, &pc->configured); } +static int sti_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_capture *result, unsigned long timeout) +{ + struct sti_pwm_chip *pc = to_sti_pwmchip(chip); + struct sti_pwm_compat_data *cdata = pc->cdata; + struct sti_cpt_ddata *ddata = pwm_get_chip_data(pwm); + struct device *dev = pc->dev; + unsigned int effective_ticks; + unsigned long long high, low; + int ret; + + if (pwm->hwpwm >= cdata->cpt_num_devs) { + dev_err(dev, "device %u is not valid\n", pwm->hwpwm); + return -EINVAL; + } + + mutex_lock(&ddata->lock); + ddata->index = 0; + + /* Prepare capture measurement */ + regmap_write(pc->regmap, PWM_CPT_EDGE(pwm->hwpwm), CPT_EDGE_RISING); + regmap_field_write(pc->pwm_cpt_int_en, BIT(pwm->hwpwm)); + + /* Enable capture */ + ret = regmap_field_write(pc->pwm_cpt_en, 1); + if (ret) { + dev_err(dev, "failed to enable PWM capture %u: %d\n", + pwm->hwpwm, ret); + goto out; + } + + ret = wait_event_interruptible_timeout(ddata->wait, ddata->index > 1, + msecs_to_jiffies(timeout)); + + regmap_write(pc->regmap, PWM_CPT_EDGE(pwm->hwpwm), CPT_EDGE_DISABLED); + + if (ret == -ERESTARTSYS) + goto out; + + switch (ddata->index) { + case 0: + case 1: + /* + * Getting here could mean: + * - input signal is constant of less than 1 Hz + * - there is no input signal at all + * + * In such case the frequency is rounded down to 0 + */ + result->period = 0; + result->duty_cycle = 0; + + break; + + case 2: + /* We have everying we need */ + high = ddata->snapshot[1] - ddata->snapshot[0]; + low = ddata->snapshot[2] - ddata->snapshot[1]; + + effective_ticks = clk_get_rate(pc->cpt_clk); + + result->period = (high + low) * NSEC_PER_SEC; + result->period /= effective_ticks; + + result->duty_cycle = high * NSEC_PER_SEC; + result->duty_cycle /= effective_ticks; + + break; + + default: + dev_err(dev, "internal error\n"); + break; + } + +out: + /* Disable capture */ + regmap_field_write(pc->pwm_cpt_en, 0); + + mutex_unlock(&ddata->lock); + return ret; +} + static const struct pwm_ops sti_pwm_ops = { + .capture = sti_pwm_capture, .config = sti_pwm_config, .enable = sti_pwm_enable, .disable = sti_pwm_disable, @@ -253,17 +404,98 @@ static const struct pwm_ops sti_pwm_ops = { .owner = THIS_MODULE, }; +static irqreturn_t sti_pwm_interrupt(int irq, void *data) +{ + struct sti_pwm_chip *pc = data; + struct device *dev = pc->dev; + struct sti_cpt_ddata *ddata; + int devicenum; + unsigned int cpt_int_stat; + unsigned int reg; + int ret = IRQ_NONE; + + ret = regmap_field_read(pc->pwm_cpt_int_stat, &cpt_int_stat); + if (ret) + return ret; + + while (cpt_int_stat) { + devicenum = ffs(cpt_int_stat) - 1; + + ddata = pwm_get_chip_data(&pc->chip.pwms[devicenum]); + + /* + * Capture input: + * _______ _______ + * | | | | + * __| |_________________| |________ + * ^0 ^1 ^2 + * + * Capture start by the first available rising edge. When a + * capture event occurs, capture value (CPT_VALx) is stored, + * index incremented, capture edge changed. + * + * After the capture, if the index > 1, we have collected the + * necessary data so we signal the thread waiting for it and + * disable the capture by setting capture edge to none + */ + + regmap_read(pc->regmap, + PWM_CPT_VAL(devicenum), + &ddata->snapshot[ddata->index]); + + switch (ddata->index) { + case 0: + case 1: + regmap_read(pc->regmap, PWM_CPT_EDGE(devicenum), ®); + reg ^= PWM_CPT_EDGE_MASK; + regmap_write(pc->regmap, PWM_CPT_EDGE(devicenum), reg); + + ddata->index++; + break; + + case 2: + regmap_write(pc->regmap, + PWM_CPT_EDGE(devicenum), + CPT_EDGE_DISABLED); + wake_up(&ddata->wait); + break; + + default: + dev_err(dev, "Internal error\n"); + } + + cpt_int_stat &= ~BIT_MASK(devicenum); + + ret = IRQ_HANDLED; + } + + /* Just ACK everything */ + regmap_write(pc->regmap, PWM_INT_ACK, PWM_INT_ACK_MASK); + + return ret; +} + static int sti_pwm_probe_dt(struct sti_pwm_chip *pc) { struct device *dev = pc->dev; const struct reg_field *reg_fields; struct device_node *np = dev->of_node; struct sti_pwm_compat_data *cdata = pc->cdata; - u32 num_chan; + u32 num_devs; + int ret; + + ret = of_property_read_u32(np, "st,pwm-num-chan", &num_devs); + if (!ret) + cdata->pwm_num_devs = num_devs; + + ret = of_property_read_u32(np, "st,capture-num-chan", &num_devs); + if (!ret) + cdata->cpt_num_devs = num_devs; - of_property_read_u32(np, "st,pwm-num-chan", &num_chan); - if (num_chan) - cdata->num_chan = num_chan; + if (!cdata->pwm_num_devs && !cdata->cpt_num_devs) { + dev_err(dev, "No channels configured\n"); + return -EINVAL; + } reg_fields = cdata->reg_fields; @@ -277,15 +509,26 @@ static int sti_pwm_probe_dt(struct sti_pwm_chip *pc) if (IS_ERR(pc->prescale_high)) return PTR_ERR(pc->prescale_high); - pc->pwm_en = devm_regmap_field_alloc(dev, pc->regmap, - reg_fields[PWM_EN]); - if (IS_ERR(pc->pwm_en)) - return PTR_ERR(pc->pwm_en); - pc->pwm_int_en = devm_regmap_field_alloc(dev, pc->regmap, - reg_fields[PWM_INT_EN]); - if (IS_ERR(pc->pwm_int_en)) - return PTR_ERR(pc->pwm_int_en); + pc->pwm_out_en = devm_regmap_field_alloc(dev, pc->regmap, + reg_fields[PWM_OUT_EN]); + if (IS_ERR(pc->pwm_out_en)) + return PTR_ERR(pc->pwm_out_en); + + pc->pwm_cpt_en = devm_regmap_field_alloc(dev, pc->regmap, + reg_fields[PWM_CPT_EN]); + if (IS_ERR(pc->pwm_cpt_en)) + return PTR_ERR(pc->pwm_cpt_en); + + pc->pwm_cpt_int_en = devm_regmap_field_alloc(dev, pc->regmap, + reg_fields[PWM_CPT_INT_EN]); + if (IS_ERR(pc->pwm_cpt_int_en)) + return PTR_ERR(pc->pwm_cpt_int_en); + + pc->pwm_cpt_int_stat = devm_regmap_field_alloc(dev, pc->regmap, + reg_fields[PWM_CPT_INT_STAT]); + if (PTR_ERR_OR_ZERO(pc->pwm_cpt_int_stat)) + return PTR_ERR(pc->pwm_cpt_int_stat); return 0; } @@ -302,7 +545,8 @@ static int sti_pwm_probe(struct platform_device *pdev) struct sti_pwm_compat_data *cdata; struct sti_pwm_chip *pc; struct resource *res; - int ret; + unsigned int i; + int irq, ret; pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL); if (!pc) @@ -323,14 +567,28 @@ static int sti_pwm_probe(struct platform_device *pdev) if (IS_ERR(pc->regmap)) return PTR_ERR(pc->regmap); + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Failed to obtain IRQ\n"); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, sti_pwm_interrupt, 0, + pdev->name, pc); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to request IRQ\n"); + return ret; + } + /* * Setup PWM data with default values: some values could be replaced * with specific ones provided from Device Tree. */ - cdata->reg_fields = &sti_pwm_regfields[0]; + cdata->reg_fields = sti_pwm_regfields; cdata->max_prescale = 0xff; - cdata->max_pwm_cnt = 255; - cdata->num_chan = 1; + cdata->max_pwm_cnt = 255; + cdata->pwm_num_devs = 0; + cdata->cpt_num_devs = 0; pc->cdata = cdata; pc->dev = dev; @@ -341,36 +599,64 @@ static int sti_pwm_probe(struct platform_device *pdev) if (ret) return ret; - pc->clk = of_clk_get_by_name(dev->of_node, "pwm"); - if (IS_ERR(pc->clk)) { + if (!cdata->pwm_num_devs) + goto skip_pwm; + + pc->pwm_clk = of_clk_get_by_name(dev->of_node, "pwm"); + if (IS_ERR(pc->pwm_clk)) { dev_err(dev, "failed to get PWM clock\n"); - return PTR_ERR(pc->clk); + return PTR_ERR(pc->pwm_clk); } - pc->clk_rate = clk_get_rate(pc->clk); - if (!pc->clk_rate) { - dev_err(dev, "failed to get clock rate\n"); - return -EINVAL; + ret = clk_prepare(pc->pwm_clk); + if (ret) { + dev_err(dev, "failed to prepare clock\n"); + return ret; } - ret = clk_prepare(pc->clk); +skip_pwm: + if (!cdata->cpt_num_devs) + goto skip_cpt; + + pc->cpt_clk = of_clk_get_by_name(dev->of_node, "capture"); + if (IS_ERR(pc->cpt_clk)) { + dev_err(dev, "failed to get PWM capture clock\n"); + return PTR_ERR(pc->cpt_clk); + } + + ret = clk_prepare(pc->cpt_clk); if (ret) { dev_err(dev, "failed to prepare clock\n"); return ret; } +skip_cpt: pc->chip.dev = dev; pc->chip.ops = &sti_pwm_ops; pc->chip.base = -1; - pc->chip.npwm = pc->cdata->num_chan; + pc->chip.npwm = pc->cdata->pwm_num_devs; pc->chip.can_sleep = true; ret = pwmchip_add(&pc->chip); if (ret < 0) { - clk_unprepare(pc->clk); + clk_unprepare(pc->pwm_clk); + clk_unprepare(pc->cpt_clk); return ret; } + for (i = 0; i < cdata->cpt_num_devs; i++) { + struct sti_cpt_ddata *ddata; + + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + init_waitqueue_head(&ddata->wait); + mutex_init(&ddata->lock); + + pwm_set_chip_data(&pc->chip.pwms[i], ddata); + } + platform_set_drvdata(pdev, pc); return 0; @@ -381,10 +667,11 @@ static int sti_pwm_remove(struct platform_device *pdev) struct sti_pwm_chip *pc = platform_get_drvdata(pdev); unsigned int i; - for (i = 0; i < pc->cdata->num_chan; i++) + for (i = 0; i < pc->cdata->pwm_num_devs; i++) pwm_disable(&pc->chip.pwms[i]); - clk_unprepare(pc->clk); + clk_unprepare(pc->pwm_clk); + clk_unprepare(pc->cpt_clk); return pwmchip_remove(&pc->chip); } diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index 03a99a53c39e..b0803f6c64d9 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -284,6 +284,12 @@ static const struct sun4i_pwm_data sun4i_pwm_data_a20 = { .npwm = 2, }; +static const struct sun4i_pwm_data sun4i_pwm_data_h3 = { + .has_prescaler_bypass = true, + .has_rdy = true, + .npwm = 1, +}; + static const struct of_device_id sun4i_pwm_dt_ids[] = { { .compatible = "allwinner,sun4i-a10-pwm", @@ -298,6 +304,9 @@ static const struct of_device_id sun4i_pwm_dt_ids[] = { .compatible = "allwinner,sun7i-a20-pwm", .data = &sun4i_pwm_data_a20, }, { + .compatible = "allwinner,sun8i-h3-pwm", + .data = &sun4i_pwm_data_h3, + }, { /* sentinel */ }, }; diff --git a/drivers/pwm/pwm-tipwmss.c b/drivers/pwm/pwm-tipwmss.c index 829f4991c96f..7fa85a1604da 100644 --- a/drivers/pwm/pwm-tipwmss.c +++ b/drivers/pwm/pwm-tipwmss.c @@ -34,7 +34,6 @@ static int pwmss_probe(struct platform_device *pdev) struct device_node *node = pdev->dev.of_node; pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); /* Populate all the child nodes here... */ ret = of_platform_populate(node, NULL, NULL, &pdev->dev); @@ -46,31 +45,13 @@ static int pwmss_probe(struct platform_device *pdev) static int pwmss_remove(struct platform_device *pdev) { - pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return 0; } -#ifdef CONFIG_PM_SLEEP -static int pwmss_suspend(struct device *dev) -{ - pm_runtime_put_sync(dev); - return 0; -} - -static int pwmss_resume(struct device *dev) -{ - pm_runtime_get_sync(dev); - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(pwmss_pm_ops, pwmss_suspend, pwmss_resume); - static struct platform_driver pwmss_driver = { .driver = { .name = "pwmss", - .pm = &pwmss_pm_ops, .of_match_table = pwmss_of_match, }, .probe = pwmss_probe, diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c index 04f76725d591..7a993b056638 100644 --- a/drivers/pwm/pwm-twl.c +++ b/drivers/pwm/pwm-twl.c @@ -269,6 +269,22 @@ static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) goto out; } + val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXEN); + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + goto out; + } + + val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXEN); + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); + if (ret < 0) { + dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); + goto out; + } + twl->twl6030_toggle3 = val; out: mutex_unlock(&twl->mutex); diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c index 18ed725594c3..0296d8178ae2 100644 --- a/drivers/pwm/sysfs.c +++ b/drivers/pwm/sysfs.c @@ -409,6 +409,24 @@ void pwmchip_sysfs_unexport(struct pwm_chip *chip) } } +void pwmchip_sysfs_unexport_children(struct pwm_chip *chip) +{ + struct device *parent; + unsigned int i; + + parent = class_find_device(&pwm_class, NULL, chip, + pwmchip_sysfs_match); + if (!parent) + return; + + for (i = 0; i < chip->npwm; i++) { + struct pwm_device *pwm = &chip->pwms[i]; + + if (test_bit(PWMF_EXPORTED, &pwm->flags)) + pwm_unexport_child(parent, pwm); + } +} + static int __init pwm_sysfs_init(void) { return class_register(&pwm_class); diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c index 3958f50c5975..e0c747aa9f85 100644 --- a/drivers/regulator/max8973-regulator.c +++ b/drivers/regulator/max8973-regulator.c @@ -495,7 +495,8 @@ static irqreturn_t max8973_thermal_irq(int irq, void *data) { struct max8973_chip *mchip = data; - thermal_zone_device_update(mchip->tz_device); + thermal_zone_device_update(mchip->tz_device, + THERMAL_EVENT_UNSPECIFIED); return IRQ_HANDLED; } diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 2d702ca6556f..a13541bdc726 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -186,7 +186,7 @@ config HISI_THERMAL config IMX_THERMAL tristate "Temperature sensor driver for Freescale i.MX SoCs" - depends on CPU_THERMAL + depends on (ARCH_MXC && CPU_THERMAL) || COMPILE_TEST depends on MFD_SYSCON depends on OF help @@ -195,6 +195,26 @@ config IMX_THERMAL cpufreq is used as the cooling device to throttle CPUs when the passive trip is crossed. +config MAX77620_THERMAL + tristate "Temperature sensor driver for Maxim MAX77620 PMIC" + depends on MFD_MAX77620 + depends on OF + help + Support for die junction temperature warning alarm for Maxim + Semiconductor PMIC MAX77620 device. Device generates two alarm + interrupts when PMIC die temperature cross the threshold of + 120 degC and 140 degC. + +config QORIQ_THERMAL + tristate "QorIQ Thermal Monitoring Unit" + depends on THERMAL_OF + depends on HAS_IOMEM + help + Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms. + It supports one critical trip point and one passive trip point. The + cpufreq is used as the cooling device to throttle CPUs when the + passive trip is crossed. + config SPEAR_THERMAL tristate "SPEAr thermal sensor driver" depends on PLAT_SPEAR || COMPILE_TEST @@ -332,6 +352,16 @@ menu "ACPI INT340X thermal drivers" source drivers/thermal/int340x_thermal/Kconfig endmenu +config INTEL_BXT_PMIC_THERMAL + tristate "Intel Broxton PMIC thermal driver" + depends on X86 && INTEL_SOC_PMIC && REGMAP + help + Select this driver for Intel Broxton PMIC with ADC channels monitoring + system temperature measurements and alerts. + This driver is used for monitoring the ADC channels of PMIC and handles + the alert trip point interrupts and notifies the thermal framework with + the trip point and temperature details of the zone. + config INTEL_PCH_THERMAL tristate "Intel PCH Thermal Reporting Driver" depends on X86 && PCI @@ -399,4 +429,9 @@ config GENERIC_ADC_THERMAL to this driver. This driver reports the temperature by reading ADC channel and converts it to temperature based on lookup table. +menu "Qualcomm thermal drivers" +depends on (ARCH_QCOM && OF) || COMPILE_TEST +source "drivers/thermal/qcom/Kconfig" +endmenu + endif diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 10b07c14f8a9..c92eb22a41ff 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -37,6 +37,8 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o +obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o +obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o @@ -45,8 +47,10 @@ obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL) += intel_quark_dts_thermal.o obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ +obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o obj-$(CONFIG_ST_THERMAL) += st/ +obj-$(CONFIG_QCOM_TSENS) += qcom/ obj-$(CONFIG_TEGRA_SOCTHERM) += tegra/ obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index a32b41783b77..9ce0e9eef923 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -74,7 +74,7 @@ struct power_table { * cpufreq frequencies. * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device. * @node: list_head to link all cpufreq_cooling_device together. - * @last_load: load measured by the latest call to cpufreq_get_actual_power() + * @last_load: load measured by the latest call to cpufreq_get_requested_power() * @time_in_idle: previous reading of the absolute time that this cpu was idle * @time_in_idle_timestamp: wall time of the last invocation of * get_cpu_idle_time_us() diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c index 652acd8fbe48..e776cea80cfc 100644 --- a/drivers/thermal/db8500_thermal.c +++ b/drivers/thermal/db8500_thermal.c @@ -306,7 +306,7 @@ static void db8500_thermal_work(struct work_struct *work) if (cur_mode == THERMAL_DEVICE_DISABLED) return; - thermal_zone_device_update(pzone->therm_dev); + thermal_zone_device_update(pzone->therm_dev, THERMAL_EVENT_UNSPECIFIED); dev_dbg(&pzone->therm_dev->device, "thermal work finished.\n"); } diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c index 01f0015f80dc..81631b110e17 100644 --- a/drivers/thermal/devfreq_cooling.c +++ b/drivers/thermal/devfreq_cooling.c @@ -312,7 +312,7 @@ static int devfreq_cooling_state2power(struct thermal_cooling_device *cdev, unsigned long freq; u32 static_power; - if (state < 0 || state >= dfc->freq_table_size) + if (state >= dfc->freq_table_size) return -EINVAL; freq = dfc->freq_table[state]; diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c index bb118a152cbb..fc5e5057f0de 100644 --- a/drivers/thermal/gov_bang_bang.c +++ b/drivers/thermal/gov_bang_bang.c @@ -65,7 +65,7 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) if (instance->target == 0 && tz->temperature >= trip_temp) instance->target = 1; else if (instance->target == 1 && - tz->temperature < trip_temp - trip_hyst) + tz->temperature <= trip_temp - trip_hyst) instance->target = 0; dev_dbg(&instance->cdev->device, "target=%d\n", diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c index 97fad8f51e1c..f6429666a1cf 100644 --- a/drivers/thermal/hisi_thermal.c +++ b/drivers/thermal/hisi_thermal.c @@ -237,7 +237,8 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) if (!data->sensors[i].tzd) continue; - thermal_zone_device_update(data->sensors[i].tzd); + thermal_zone_device_update(data->sensors[i].tzd, + THERMAL_EVENT_UNSPECIFIED); } return IRQ_HANDLED; diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index e473548b5d28..06912f0602b7 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -246,7 +246,7 @@ static int imx_set_mode(struct thermal_zone_device *tz, } data->mode = mode; - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); return 0; } @@ -457,7 +457,7 @@ static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev) dev_dbg(&data->tz->device, "THERMAL ALARM: T > %d\n", data->alarm_temp / 1000); - thermal_zone_device_update(data->tz); + thermal_zone_device_update(data->tz, THERMAL_EVENT_UNSPECIFIED); return IRQ_HANDLED; } diff --git a/drivers/thermal/int340x_thermal/int3402_thermal.c b/drivers/thermal/int340x_thermal/int3402_thermal.c index 69df3d960303..8e90b3151a42 100644 --- a/drivers/thermal/int340x_thermal/int3402_thermal.c +++ b/drivers/thermal/int340x_thermal/int3402_thermal.c @@ -35,7 +35,8 @@ static void int3402_notify(acpi_handle handle, u32 event, void *data) case INT3402_PERF_CHANGED_EVENT: break; case INT3402_THERMAL_EVENT: - int340x_thermal_zone_device_update(priv->int340x_zone); + int340x_thermal_zone_device_update(priv->int340x_zone, + THERMAL_TRIP_VIOLATED); break; default: break; diff --git a/drivers/thermal/int340x_thermal/int3403_thermal.c b/drivers/thermal/int340x_thermal/int3403_thermal.c index 50a7a08e3a15..c4890c9437eb 100644 --- a/drivers/thermal/int340x_thermal/int3403_thermal.c +++ b/drivers/thermal/int340x_thermal/int3403_thermal.c @@ -25,6 +25,7 @@ #define INT3403_TYPE_CHARGER 0x0B #define INT3403_TYPE_BATTERY 0x0C #define INT3403_PERF_CHANGED_EVENT 0x80 +#define INT3403_PERF_TRIP_POINT_CHANGED 0x81 #define INT3403_THERMAL_EVENT 0x90 /* Preserved structure for future expandbility */ @@ -72,7 +73,13 @@ static void int3403_notify(acpi_handle handle, case INT3403_PERF_CHANGED_EVENT: break; case INT3403_THERMAL_EVENT: - int340x_thermal_zone_device_update(obj->int340x_zone); + int340x_thermal_zone_device_update(obj->int340x_zone, + THERMAL_TRIP_VIOLATED); + break; + case INT3403_PERF_TRIP_POINT_CHANGED: + int340x_thermal_read_trips(obj->int340x_zone); + int340x_thermal_zone_device_update(obj->int340x_zone, + THERMAL_TRIP_CHANGED); break; default: dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event); diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c index b9b2666aa94c..145a5c53ff5c 100644 --- a/drivers/thermal/int340x_thermal/int340x_thermal_zone.c +++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.c @@ -177,6 +177,42 @@ static int int340x_thermal_get_trip_config(acpi_handle handle, char *name, return 0; } +int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone) +{ + int trip_cnt = int34x_zone->aux_trip_nr; + int i; + + int34x_zone->crt_trip_id = -1; + if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_CRT", + &int34x_zone->crt_temp)) + int34x_zone->crt_trip_id = trip_cnt++; + + int34x_zone->hot_trip_id = -1; + if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_HOT", + &int34x_zone->hot_temp)) + int34x_zone->hot_trip_id = trip_cnt++; + + int34x_zone->psv_trip_id = -1; + if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_PSV", + &int34x_zone->psv_temp)) + int34x_zone->psv_trip_id = trip_cnt++; + + for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) { + char name[5] = { '_', 'A', 'C', '0' + i, '\0' }; + + if (int340x_thermal_get_trip_config(int34x_zone->adev->handle, + name, + &int34x_zone->act_trips[i].temp)) + break; + + int34x_zone->act_trips[i].id = trip_cnt++; + int34x_zone->act_trips[i].valid = true; + } + + return trip_cnt; +} +EXPORT_SYMBOL_GPL(int340x_thermal_read_trips); + static struct thermal_zone_params int340x_thermal_params = { .governor_name = "user_space", .no_hwmon = true, @@ -188,7 +224,7 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev, struct int34x_thermal_zone *int34x_thermal_zone; acpi_status status; unsigned long long trip_cnt; - int trip_mask = 0, i; + int trip_mask = 0; int ret; int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone), @@ -214,28 +250,8 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev, int34x_thermal_zone->aux_trip_nr = trip_cnt; } - int34x_thermal_zone->crt_trip_id = -1; - if (!int340x_thermal_get_trip_config(adev->handle, "_CRT", - &int34x_thermal_zone->crt_temp)) - int34x_thermal_zone->crt_trip_id = trip_cnt++; - int34x_thermal_zone->hot_trip_id = -1; - if (!int340x_thermal_get_trip_config(adev->handle, "_HOT", - &int34x_thermal_zone->hot_temp)) - int34x_thermal_zone->hot_trip_id = trip_cnt++; - int34x_thermal_zone->psv_trip_id = -1; - if (!int340x_thermal_get_trip_config(adev->handle, "_PSV", - &int34x_thermal_zone->psv_temp)) - int34x_thermal_zone->psv_trip_id = trip_cnt++; - for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) { - char name[5] = { '_', 'A', 'C', '0' + i, '\0' }; + trip_cnt = int340x_thermal_read_trips(int34x_thermal_zone); - if (int340x_thermal_get_trip_config(adev->handle, name, - &int34x_thermal_zone->act_trips[i].temp)) - break; - - int34x_thermal_zone->act_trips[i].id = trip_cnt++; - int34x_thermal_zone->act_trips[i].valid = true; - } int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table( adev->handle); diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.h b/drivers/thermal/int340x_thermal/int340x_thermal_zone.h index aaadf724ff2e..5f3ba4775c5c 100644 --- a/drivers/thermal/int340x_thermal/int340x_thermal_zone.h +++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.h @@ -46,6 +46,7 @@ struct int34x_thermal_zone { struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *, struct thermal_zone_device_ops *override_ops); void int340x_thermal_zone_remove(struct int34x_thermal_zone *); +int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone); static inline void int340x_thermal_zone_set_priv_data( struct int34x_thermal_zone *tzone, void *priv_data) @@ -60,9 +61,10 @@ static inline void *int340x_thermal_zone_get_priv_data( } static inline void int340x_thermal_zone_device_update( - struct int34x_thermal_zone *tzone) + struct int34x_thermal_zone *tzone, + enum thermal_notify_event event) { - thermal_zone_device_update(tzone->zone); + thermal_zone_device_update(tzone->zone, event); } #endif diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c index 42c1ac057bad..ff3b36f339e3 100644 --- a/drivers/thermal/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c @@ -258,7 +258,8 @@ static void proc_thermal_notify(acpi_handle handle, u32 event, void *data) switch (event) { case PROC_POWER_CAPABILITY_CHANGED: proc_thermal_read_ppcc(proc_priv); - int340x_thermal_zone_device_update(proc_priv->int340x_zone); + int340x_thermal_zone_device_update(proc_priv->int340x_zone, + THERMAL_DEVICE_POWER_CAPABILITY_CHANGED); break; default: dev_err(proc_priv->dev, "Unsupported event [0x%x]\n", event); diff --git a/drivers/thermal/intel_bxt_pmic_thermal.c b/drivers/thermal/intel_bxt_pmic_thermal.c new file mode 100644 index 000000000000..0f19a393ddd8 --- /dev/null +++ b/drivers/thermal/intel_bxt_pmic_thermal.c @@ -0,0 +1,300 @@ +/* + * Intel Broxton PMIC thermal driver + * + * Copyright (C) 2016 Intel Corporation. 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. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/thermal.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/mfd/intel_soc_pmic.h> + +#define BXTWC_THRM0IRQ 0x4E04 +#define BXTWC_THRM1IRQ 0x4E05 +#define BXTWC_THRM2IRQ 0x4E06 +#define BXTWC_MTHRM0IRQ 0x4E12 +#define BXTWC_MTHRM1IRQ 0x4E13 +#define BXTWC_MTHRM2IRQ 0x4E14 +#define BXTWC_STHRM0IRQ 0x4F19 +#define BXTWC_STHRM1IRQ 0x4F1A +#define BXTWC_STHRM2IRQ 0x4F1B + +struct trip_config_map { + u16 irq_reg; + u16 irq_en; + u16 evt_stat; + u8 irq_mask; + u8 irq_en_mask; + u8 evt_mask; + u8 trip_num; +}; + +struct thermal_irq_map { + char handle[20]; + int num_trips; + const struct trip_config_map *trip_config; +}; + +struct pmic_thermal_data { + const struct thermal_irq_map *maps; + int num_maps; +}; + +static const struct trip_config_map bxtwc_str0_trip_config[] = { + { + .irq_reg = BXTWC_THRM0IRQ, + .irq_mask = 0x01, + .irq_en = BXTWC_MTHRM0IRQ, + .irq_en_mask = 0x01, + .evt_stat = BXTWC_STHRM0IRQ, + .evt_mask = 0x01, + .trip_num = 0 + }, + { + .irq_reg = BXTWC_THRM0IRQ, + .irq_mask = 0x10, + .irq_en = BXTWC_MTHRM0IRQ, + .irq_en_mask = 0x10, + .evt_stat = BXTWC_STHRM0IRQ, + .evt_mask = 0x10, + .trip_num = 1 + } +}; + +static const struct trip_config_map bxtwc_str1_trip_config[] = { + { + .irq_reg = BXTWC_THRM0IRQ, + .irq_mask = 0x02, + .irq_en = BXTWC_MTHRM0IRQ, + .irq_en_mask = 0x02, + .evt_stat = BXTWC_STHRM0IRQ, + .evt_mask = 0x02, + .trip_num = 0 + }, + { + .irq_reg = BXTWC_THRM0IRQ, + .irq_mask = 0x20, + .irq_en = BXTWC_MTHRM0IRQ, + .irq_en_mask = 0x20, + .evt_stat = BXTWC_STHRM0IRQ, + .evt_mask = 0x20, + .trip_num = 1 + }, +}; + +static const struct trip_config_map bxtwc_str2_trip_config[] = { + { + .irq_reg = BXTWC_THRM0IRQ, + .irq_mask = 0x04, + .irq_en = BXTWC_MTHRM0IRQ, + .irq_en_mask = 0x04, + .evt_stat = BXTWC_STHRM0IRQ, + .evt_mask = 0x04, + .trip_num = 0 + }, + { + .irq_reg = BXTWC_THRM0IRQ, + .irq_mask = 0x40, + .irq_en = BXTWC_MTHRM0IRQ, + .irq_en_mask = 0x40, + .evt_stat = BXTWC_STHRM0IRQ, + .evt_mask = 0x40, + .trip_num = 1 + }, +}; + +static const struct trip_config_map bxtwc_str3_trip_config[] = { + { + .irq_reg = BXTWC_THRM2IRQ, + .irq_mask = 0x10, + .irq_en = BXTWC_MTHRM2IRQ, + .irq_en_mask = 0x10, + .evt_stat = BXTWC_STHRM2IRQ, + .evt_mask = 0x10, + .trip_num = 0 + }, +}; + +static const struct thermal_irq_map bxtwc_thermal_irq_map[] = { + { + .handle = "STR0", + .trip_config = bxtwc_str0_trip_config, + .num_trips = ARRAY_SIZE(bxtwc_str0_trip_config), + }, + { + .handle = "STR1", + .trip_config = bxtwc_str1_trip_config, + .num_trips = ARRAY_SIZE(bxtwc_str1_trip_config), + }, + { + .handle = "STR2", + .trip_config = bxtwc_str2_trip_config, + .num_trips = ARRAY_SIZE(bxtwc_str2_trip_config), + }, + { + .handle = "STR3", + .trip_config = bxtwc_str3_trip_config, + .num_trips = ARRAY_SIZE(bxtwc_str3_trip_config), + }, +}; + +static const struct pmic_thermal_data bxtwc_thermal_data = { + .maps = bxtwc_thermal_irq_map, + .num_maps = ARRAY_SIZE(bxtwc_thermal_irq_map), +}; + +static irqreturn_t pmic_thermal_irq_handler(int irq, void *data) +{ + struct platform_device *pdev = data; + struct thermal_zone_device *tzd; + struct pmic_thermal_data *td; + struct intel_soc_pmic *pmic; + struct regmap *regmap; + u8 reg_val, mask, irq_stat, trip; + u16 reg, evt_stat_reg; + int i, j, ret; + + pmic = dev_get_drvdata(pdev->dev.parent); + regmap = pmic->regmap; + td = (struct pmic_thermal_data *) + platform_get_device_id(pdev)->driver_data; + + /* Resolve thermal irqs */ + for (i = 0; i < td->num_maps; i++) { + for (j = 0; j < td->maps[i].num_trips; j++) { + reg = td->maps[i].trip_config[j].irq_reg; + mask = td->maps[i].trip_config[j].irq_mask; + /* + * Read the irq register to resolve whether the + * interrupt was triggered for this sensor + */ + if (regmap_read(regmap, reg, &ret)) + return IRQ_HANDLED; + + reg_val = (u8)ret; + irq_stat = ((u8)ret & mask); + + if (!irq_stat) + continue; + + /* + * Read the status register to find out what + * event occurred i.e a high or a low + */ + evt_stat_reg = td->maps[i].trip_config[j].evt_stat; + if (regmap_read(regmap, evt_stat_reg, &ret)) + return IRQ_HANDLED; + + trip = td->maps[i].trip_config[j].trip_num; + tzd = thermal_zone_get_zone_by_name(td->maps[i].handle); + if (!IS_ERR(tzd)) + thermal_zone_device_update(tzd, + THERMAL_EVENT_UNSPECIFIED); + + /* Clear the appropriate irq */ + regmap_write(regmap, reg, reg_val & mask); + } + } + + return IRQ_HANDLED; +} + +static int pmic_thermal_probe(struct platform_device *pdev) +{ + struct regmap_irq_chip_data *regmap_irq_chip; + struct pmic_thermal_data *thermal_data; + int ret, irq, virq, i, j, pmic_irq_count; + struct intel_soc_pmic *pmic; + struct regmap *regmap; + struct device *dev; + u16 reg; + u8 mask; + + dev = &pdev->dev; + pmic = dev_get_drvdata(pdev->dev.parent); + if (!pmic) { + dev_err(dev, "Failed to get struct intel_soc_pmic pointer\n"); + return -ENODEV; + } + + thermal_data = (struct pmic_thermal_data *) + platform_get_device_id(pdev)->driver_data; + if (!thermal_data) { + dev_err(dev, "No thermal data initialized!!\n"); + return -ENODEV; + } + + regmap = pmic->regmap; + regmap_irq_chip = pmic->irq_chip_data_level2; + + pmic_irq_count = 0; + while ((irq = platform_get_irq(pdev, pmic_irq_count)) != -ENXIO) { + virq = regmap_irq_get_virq(regmap_irq_chip, irq); + if (virq < 0) { + dev_err(dev, "failed to get virq by irq %d\n", irq); + return virq; + } + + ret = devm_request_threaded_irq(&pdev->dev, virq, + NULL, pmic_thermal_irq_handler, + IRQF_ONESHOT, "pmic_thermal", pdev); + + if (ret) { + dev_err(dev, "request irq(%d) failed: %d\n", virq, ret); + return ret; + } + pmic_irq_count++; + } + + /* Enable thermal interrupts */ + for (i = 0; i < thermal_data->num_maps; i++) { + for (j = 0; j < thermal_data->maps[i].num_trips; j++) { + reg = thermal_data->maps[i].trip_config[j].irq_en; + mask = thermal_data->maps[i].trip_config[j].irq_en_mask; + ret = regmap_update_bits(regmap, reg, mask, 0x00); + if (ret) + return ret; + } + } + + return 0; +} + +static const struct platform_device_id pmic_thermal_id_table[] = { + { + .name = "bxt_wcove_thermal", + .driver_data = (kernel_ulong_t)&bxtwc_thermal_data, + }, + {}, +}; + +static struct platform_driver pmic_thermal_driver = { + .probe = pmic_thermal_probe, + .driver = { + .name = "pmic_thermal", + }, + .id_table = pmic_thermal_id_table, +}; + +MODULE_DEVICE_TABLE(platform, pmic_thermal_id_table); +module_platform_driver(pmic_thermal_driver); + +MODULE_AUTHOR("Yegnesh S Iyer <yegnesh.s.iyer@intel.com>"); +MODULE_DESCRIPTION("Intel Broxton PMIC Thermal Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thermal/intel_soc_dts_iosf.c b/drivers/thermal/intel_soc_dts_iosf.c index f72e1db3216f..e0813dfaa278 100644 --- a/drivers/thermal/intel_soc_dts_iosf.c +++ b/drivers/thermal/intel_soc_dts_iosf.c @@ -391,7 +391,8 @@ void intel_soc_dts_iosf_interrupt_handler(struct intel_soc_dts_sensors *sensors) for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) { pr_debug("TZD update for zone %d\n", i); - thermal_zone_device_update(sensors->soc_dts[i].tzone); + thermal_zone_device_update(sensors->soc_dts[i].tzone, + THERMAL_EVENT_UNSPECIFIED); } } else spin_unlock_irqrestore(&sensors->intr_notify_lock, flags); diff --git a/drivers/thermal/max77620_thermal.c b/drivers/thermal/max77620_thermal.c new file mode 100644 index 000000000000..83905ff46e40 --- /dev/null +++ b/drivers/thermal/max77620_thermal.c @@ -0,0 +1,166 @@ +/* + * Junction temperature thermal driver for Maxim Max77620. + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Author: Laxman Dewangan <ldewangan@nvidia.com> + * Mallikarjun Kasoju <mkasoju@nvidia.com> + * + * 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. + */ + +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/mfd/max77620.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/thermal.h> + +#define MAX77620_NORMAL_OPERATING_TEMP 100000 +#define MAX77620_TJALARM1_TEMP 120000 +#define MAX77620_TJALARM2_TEMP 140000 + +struct max77620_therm_info { + struct device *dev; + struct regmap *rmap; + struct thermal_zone_device *tz_device; + int irq_tjalarm1; + int irq_tjalarm2; +}; + +/** + * max77620_thermal_read_temp: Read PMIC die temperatue. + * @data: Device specific data. + * temp: Temperature in millidegrees Celsius + * + * The actual temperature of PMIC die is not available from PMIC. + * PMIC only tells the status if it has crossed or not the threshold level + * of 120degC or 140degC. + * If threshold has not been crossed then assume die temperature as 100degC + * else 120degC or 140deG based on the PMIC die temp threshold status. + * + * Return 0 on success otherwise error number to show reason of failure. + */ + +static int max77620_thermal_read_temp(void *data, int *temp) +{ + struct max77620_therm_info *mtherm = data; + unsigned int val; + int ret; + + ret = regmap_read(mtherm->rmap, MAX77620_REG_STATLBT, &val); + if (ret < 0) { + dev_err(mtherm->dev, "Failed to read STATLBT: %d\n", ret); + return ret; + } + + if (val & MAX77620_IRQ_TJALRM2_MASK) + *temp = MAX77620_TJALARM2_TEMP; + else if (val & MAX77620_IRQ_TJALRM1_MASK) + *temp = MAX77620_TJALARM1_TEMP; + else + *temp = MAX77620_NORMAL_OPERATING_TEMP; + + return 0; +} + +static const struct thermal_zone_of_device_ops max77620_thermal_ops = { + .get_temp = max77620_thermal_read_temp, +}; + +static irqreturn_t max77620_thermal_irq(int irq, void *data) +{ + struct max77620_therm_info *mtherm = data; + + if (irq == mtherm->irq_tjalarm1) + dev_warn(mtherm->dev, "Junction Temp Alarm1(120C) occurred\n"); + else if (irq == mtherm->irq_tjalarm2) + dev_crit(mtherm->dev, "Junction Temp Alarm2(140C) occurred\n"); + + thermal_zone_device_update(mtherm->tz_device, + THERMAL_EVENT_UNSPECIFIED); + + return IRQ_HANDLED; +} + +static int max77620_thermal_probe(struct platform_device *pdev) +{ + struct max77620_therm_info *mtherm; + int ret; + + mtherm = devm_kzalloc(&pdev->dev, sizeof(*mtherm), GFP_KERNEL); + if (!mtherm) + return -ENOMEM; + + mtherm->irq_tjalarm1 = platform_get_irq(pdev, 0); + mtherm->irq_tjalarm2 = platform_get_irq(pdev, 1); + if ((mtherm->irq_tjalarm1 < 0) || (mtherm->irq_tjalarm2 < 0)) { + dev_err(&pdev->dev, "Alarm irq number not available\n"); + return -EINVAL; + } + + pdev->dev.of_node = pdev->dev.parent->of_node; + + mtherm->dev = &pdev->dev; + mtherm->rmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!mtherm->rmap) { + dev_err(&pdev->dev, "Failed to get parent regmap\n"); + return -ENODEV; + } + + mtherm->tz_device = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, + mtherm, &max77620_thermal_ops); + if (IS_ERR(mtherm->tz_device)) { + ret = PTR_ERR(mtherm->tz_device); + dev_err(&pdev->dev, "Failed to register thermal zone: %d\n", + ret); + return ret; + } + + ret = devm_request_threaded_irq(&pdev->dev, mtherm->irq_tjalarm1, NULL, + max77620_thermal_irq, + IRQF_ONESHOT | IRQF_SHARED, + dev_name(&pdev->dev), mtherm); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to request irq1: %d\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(&pdev->dev, mtherm->irq_tjalarm2, NULL, + max77620_thermal_irq, + IRQF_ONESHOT | IRQF_SHARED, + dev_name(&pdev->dev), mtherm); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to request irq2: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, mtherm); + + return 0; +} + +static struct platform_device_id max77620_thermal_devtype[] = { + { .name = "max77620-thermal", }, + {}, +}; + +static struct platform_driver max77620_thermal_driver = { + .driver = { + .name = "max77620-thermal", + }, + .probe = max77620_thermal_probe, + .id_table = max77620_thermal_devtype, +}; + +module_platform_driver(max77620_thermal_driver); + +MODULE_DESCRIPTION("Max77620 Junction temperature Thermal driver"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_AUTHOR("Mallikarjun Kasoju <mkasoju@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thermal/mtk_thermal.c b/drivers/thermal/mtk_thermal.c index 262ab0a2266f..34169c32d495 100644 --- a/drivers/thermal/mtk_thermal.c +++ b/drivers/thermal/mtk_thermal.c @@ -2,6 +2,7 @@ * Copyright (c) 2015 MediaTek Inc. * Author: Hanyi Wu <hanyi.wu@mediatek.com> * Sascha Hauer <s.hauer@pengutronix.de> + * Dawei Chien <dawei.chien@mediatek.com> * * 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 @@ -21,6 +22,7 @@ #include <linux/nvmem-consumer.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/io.h> @@ -88,6 +90,7 @@ #define TEMP_ADCVALIDMASK_VALID_HIGH BIT(5) #define TEMP_ADCVALIDMASK_VALID_POS(bit) (bit) +/* MT8173 thermal sensors */ #define MT8173_TS1 0 #define MT8173_TS2 1 #define MT8173_TS3 2 @@ -106,7 +109,12 @@ /* The number of sensing points per bank */ #define MT8173_NUM_SENSORS_PER_ZONE 4 -/* Layout of the fuses providing the calibration data */ +/* + * Layout of the fuses providing the calibration data + * These macros could be used for both MT8173 and MT2701. + * MT8173 has five sensors and need five VTS calibration data, + * and MT2701 has three sensors and need three VTS calibration data. + */ #define MT8173_CALIB_BUF0_VALID BIT(0) #define MT8173_CALIB_BUF1_ADC_GE(x) (((x) >> 22) & 0x3ff) #define MT8173_CALIB_BUF0_VTS_TS1(x) (((x) >> 17) & 0x1ff) @@ -117,24 +125,50 @@ #define MT8173_CALIB_BUF0_DEGC_CALI(x) (((x) >> 1) & 0x3f) #define MT8173_CALIB_BUF0_O_SLOPE(x) (((x) >> 26) & 0x3f) +/* MT2701 thermal sensors */ +#define MT2701_TS1 0 +#define MT2701_TS2 1 +#define MT2701_TSABB 2 + +/* AUXADC channel 11 is used for the temperature sensors */ +#define MT2701_TEMP_AUXADC_CHANNEL 11 + +/* The total number of temperature sensors in the MT2701 */ +#define MT2701_NUM_SENSORS 3 + #define THERMAL_NAME "mtk-thermal" +/* The number of sensing points per bank */ +#define MT2701_NUM_SENSORS_PER_ZONE 3 + struct mtk_thermal; +struct thermal_bank_cfg { + unsigned int num_sensors; + const int *sensors; +}; + struct mtk_thermal_bank { struct mtk_thermal *mt; int id; }; +struct mtk_thermal_data { + s32 num_banks; + s32 num_sensors; + s32 auxadc_channel; + const int *sensor_mux_values; + const int *msr; + const int *adcpnp; + struct thermal_bank_cfg bank_data[]; +}; + struct mtk_thermal { struct device *dev; void __iomem *thermal_base; struct clk *clk_peri_therm; struct clk *clk_auxadc; - - struct mtk_thermal_bank banks[MT8173_NUM_ZONES]; - /* lock: for getting and putting banks */ struct mutex lock; @@ -144,16 +178,44 @@ struct mtk_thermal { s32 o_slope; s32 vts[MT8173_NUM_SENSORS]; + const struct mtk_thermal_data *conf; + struct mtk_thermal_bank banks[]; }; -struct mtk_thermal_bank_cfg { - unsigned int num_sensors; - unsigned int sensors[MT8173_NUM_SENSORS_PER_ZONE]; +/* MT8173 thermal sensor data */ +const int mt8173_bank_data[MT8173_NUM_ZONES][3] = { + { MT8173_TS2, MT8173_TS3 }, + { MT8173_TS2, MT8173_TS4 }, + { MT8173_TS1, MT8173_TS2, MT8173_TSABB }, + { MT8173_TS2 }, }; -static const int sensor_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 }; +const int mt8173_msr[MT8173_NUM_SENSORS_PER_ZONE] = { + TEMP_MSR0, TEMP_MSR1, TEMP_MSR2, TEMP_MSR2 +}; -/* +const int mt8173_adcpnp[MT8173_NUM_SENSORS_PER_ZONE] = { + TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2, TEMP_ADCPNP3 +}; + +const int mt8173_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 }; + +/* MT2701 thermal sensor data */ +const int mt2701_bank_data[MT2701_NUM_SENSORS] = { + MT2701_TS1, MT2701_TS2, MT2701_TSABB +}; + +const int mt2701_msr[MT2701_NUM_SENSORS_PER_ZONE] = { + TEMP_MSR0, TEMP_MSR1, TEMP_MSR2 +}; + +const int mt2701_adcpnp[MT2701_NUM_SENSORS_PER_ZONE] = { + TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2 +}; + +const int mt2701_mux_values[MT2701_NUM_SENSORS] = { 0, 1, 16 }; + +/** * The MT8173 thermal controller has four banks. Each bank can read up to * four temperature sensors simultaneously. The MT8173 has a total of 5 * temperature sensors. We use each bank to measure a certain area of the @@ -166,42 +228,53 @@ static const int sensor_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 }; * data, and this indeed needs the temperatures of the individual banks * for making better decisions. */ -static const struct mtk_thermal_bank_cfg bank_data[] = { - { - .num_sensors = 2, - .sensors = { MT8173_TS2, MT8173_TS3 }, - }, { - .num_sensors = 2, - .sensors = { MT8173_TS2, MT8173_TS4 }, - }, { - .num_sensors = 3, - .sensors = { MT8173_TS1, MT8173_TS2, MT8173_TSABB }, - }, { - .num_sensors = 1, - .sensors = { MT8173_TS2 }, +static const struct mtk_thermal_data mt8173_thermal_data = { + .auxadc_channel = MT8173_TEMP_AUXADC_CHANNEL, + .num_banks = MT8173_NUM_ZONES, + .num_sensors = MT8173_NUM_SENSORS, + .bank_data = { + { + .num_sensors = 2, + .sensors = mt8173_bank_data[0], + }, { + .num_sensors = 2, + .sensors = mt8173_bank_data[1], + }, { + .num_sensors = 3, + .sensors = mt8173_bank_data[2], + }, { + .num_sensors = 1, + .sensors = mt8173_bank_data[3], + }, }, + .msr = mt8173_msr, + .adcpnp = mt8173_adcpnp, + .sensor_mux_values = mt8173_mux_values, }; -struct mtk_thermal_sense_point { - int msr; - int adcpnp; -}; - -static const struct mtk_thermal_sense_point - sensing_points[MT8173_NUM_SENSORS_PER_ZONE] = { - { - .msr = TEMP_MSR0, - .adcpnp = TEMP_ADCPNP0, - }, { - .msr = TEMP_MSR1, - .adcpnp = TEMP_ADCPNP1, - }, { - .msr = TEMP_MSR2, - .adcpnp = TEMP_ADCPNP2, - }, { - .msr = TEMP_MSR3, - .adcpnp = TEMP_ADCPNP3, +/** + * The MT2701 thermal controller has one bank, which can read up to + * three temperature sensors simultaneously. The MT2701 has a total of 3 + * temperature sensors. + * + * The thermal core only gets the maximum temperature of this one bank, + * so the bank concept wouldn't be necessary here. However, the SVS (Smart + * Voltage Scaling) unit makes its decisions based on the same bank + * data. + */ +static const struct mtk_thermal_data mt2701_thermal_data = { + .auxadc_channel = MT2701_TEMP_AUXADC_CHANNEL, + .num_banks = 1, + .num_sensors = MT2701_NUM_SENSORS, + .bank_data = { + { + .num_sensors = 3, + .sensors = mt2701_bank_data, + }, }, + .msr = mt2701_msr, + .adcpnp = mt2701_adcpnp, + .sensor_mux_values = mt2701_mux_values, }; /** @@ -270,13 +343,16 @@ static void mtk_thermal_put_bank(struct mtk_thermal_bank *bank) static int mtk_thermal_bank_temperature(struct mtk_thermal_bank *bank) { struct mtk_thermal *mt = bank->mt; + const struct mtk_thermal_data *conf = mt->conf; int i, temp = INT_MIN, max = INT_MIN; u32 raw; - for (i = 0; i < bank_data[bank->id].num_sensors; i++) { - raw = readl(mt->thermal_base + sensing_points[i].msr); + for (i = 0; i < conf->bank_data[bank->id].num_sensors; i++) { + raw = readl(mt->thermal_base + conf->msr[i]); - temp = raw_to_mcelsius(mt, bank_data[bank->id].sensors[i], raw); + temp = raw_to_mcelsius(mt, + conf->bank_data[bank->id].sensors[i], + raw); /* * The first read of a sensor often contains very high bogus @@ -299,7 +375,7 @@ static int mtk_read_temp(void *data, int *temperature) int i; int tempmax = INT_MIN; - for (i = 0; i < MT8173_NUM_ZONES; i++) { + for (i = 0; i < mt->conf->num_banks; i++) { struct mtk_thermal_bank *bank = &mt->banks[i]; mtk_thermal_get_bank(bank); @@ -322,7 +398,7 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num, u32 apmixed_phys_base, u32 auxadc_phys_base) { struct mtk_thermal_bank *bank = &mt->banks[num]; - const struct mtk_thermal_bank_cfg *cfg = &bank_data[num]; + const struct mtk_thermal_data *conf = mt->conf; int i; bank->id = num; @@ -368,7 +444,7 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num, * this value will be stored to TEMP_PNPMUXADDR (TEMP_SPARE0) * automatically by hw */ - writel(BIT(MT8173_TEMP_AUXADC_CHANNEL), mt->thermal_base + TEMP_ADCMUX); + writel(BIT(conf->auxadc_channel), mt->thermal_base + TEMP_ADCMUX); /* AHB address for auxadc mux selection */ writel(auxadc_phys_base + AUXADC_CON1_CLR_V, @@ -379,18 +455,18 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num, mt->thermal_base + TEMP_PNPMUXADDR); /* AHB value for auxadc enable */ - writel(BIT(MT8173_TEMP_AUXADC_CHANNEL), mt->thermal_base + TEMP_ADCEN); + writel(BIT(conf->auxadc_channel), mt->thermal_base + TEMP_ADCEN); /* AHB address for auxadc enable (channel 0 immediate mode selected) */ writel(auxadc_phys_base + AUXADC_CON1_SET_V, mt->thermal_base + TEMP_ADCENADDR); /* AHB address for auxadc valid bit */ - writel(auxadc_phys_base + AUXADC_DATA(MT8173_TEMP_AUXADC_CHANNEL), + writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel), mt->thermal_base + TEMP_ADCVALIDADDR); /* AHB address for auxadc voltage output */ - writel(auxadc_phys_base + AUXADC_DATA(MT8173_TEMP_AUXADC_CHANNEL), + writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel), mt->thermal_base + TEMP_ADCVOLTADDR); /* read valid & voltage are at the same register */ @@ -407,11 +483,12 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num, writel(TEMP_ADCWRITECTRL_ADC_MUX_WRITE, mt->thermal_base + TEMP_ADCWRITECTRL); - for (i = 0; i < cfg->num_sensors; i++) - writel(sensor_mux_values[cfg->sensors[i]], - mt->thermal_base + sensing_points[i].adcpnp); + for (i = 0; i < conf->bank_data[num].num_sensors; i++) + writel(conf->sensor_mux_values[conf->bank_data[num].sensors[i]], + mt->thermal_base + conf->adcpnp[i]); - writel((1 << cfg->num_sensors) - 1, mt->thermal_base + TEMP_MONCTL0); + writel((1 << conf->bank_data[num].num_sensors) - 1, + mt->thermal_base + TEMP_MONCTL0); writel(TEMP_ADCWRITECTRL_ADC_PNP_WRITE | TEMP_ADCWRITECTRL_ADC_MUX_WRITE, @@ -442,7 +519,7 @@ static int mtk_thermal_get_calibration_data(struct device *dev, /* Start with default values */ mt->adc_ge = 512; - for (i = 0; i < MT8173_NUM_SENSORS; i++) + for (i = 0; i < mt->conf->num_sensors; i++) mt->vts[i] = 260; mt->degc_cali = 40; mt->o_slope = 0; @@ -486,18 +563,37 @@ out: return ret; } +static const struct of_device_id mtk_thermal_of_match[] = { + { + .compatible = "mediatek,mt8173-thermal", + .data = (void *)&mt8173_thermal_data, + }, + { + .compatible = "mediatek,mt2701-thermal", + .data = (void *)&mt2701_thermal_data, + }, { + }, +}; +MODULE_DEVICE_TABLE(of, mtk_thermal_of_match); + static int mtk_thermal_probe(struct platform_device *pdev) { int ret, i; struct device_node *auxadc, *apmixedsys, *np = pdev->dev.of_node; struct mtk_thermal *mt; struct resource *res; + const struct of_device_id *of_id; u64 auxadc_phys_base, apmixed_phys_base; + struct thermal_zone_device *tzdev; mt = devm_kzalloc(&pdev->dev, sizeof(*mt), GFP_KERNEL); if (!mt) return -ENOMEM; + of_id = of_match_device(mtk_thermal_of_match, &pdev->dev); + if (of_id) + mt->conf = (const struct mtk_thermal_data *)of_id->data; + mt->clk_peri_therm = devm_clk_get(&pdev->dev, "therm"); if (IS_ERR(mt->clk_peri_therm)) return PTR_ERR(mt->clk_peri_therm); @@ -565,17 +661,23 @@ static int mtk_thermal_probe(struct platform_device *pdev) goto err_disable_clk_auxadc; } - for (i = 0; i < MT8173_NUM_ZONES; i++) + for (i = 0; i < mt->conf->num_banks; i++) mtk_thermal_init_bank(mt, i, apmixed_phys_base, auxadc_phys_base); platform_set_drvdata(pdev, mt); - devm_thermal_zone_of_sensor_register(&pdev->dev, 0, mt, - &mtk_thermal_ops); + tzdev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, mt, + &mtk_thermal_ops); + if (IS_ERR(tzdev)) { + ret = PTR_ERR(tzdev); + goto err_disable_clk_peri_therm; + } return 0; +err_disable_clk_peri_therm: + clk_disable_unprepare(mt->clk_peri_therm); err_disable_clk_auxadc: clk_disable_unprepare(mt->clk_auxadc); @@ -592,13 +694,6 @@ static int mtk_thermal_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id mtk_thermal_of_match[] = { - { - .compatible = "mediatek,mt8173-thermal", - }, { - }, -}; - static struct platform_driver mtk_thermal_driver = { .probe = mtk_thermal_probe, .remove = mtk_thermal_remove, @@ -610,6 +705,7 @@ static struct platform_driver mtk_thermal_driver = { module_platform_driver(mtk_thermal_driver); +MODULE_AUTHOR("Dawei Chien <dawei.chien@mediatek.com>"); MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); MODULE_AUTHOR("Hanyi Wu <hanyi.wu@mediatek.com>"); MODULE_DESCRIPTION("Mediatek thermal driver"); diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index b8e509c60848..d04ec3b9e5ff 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -101,6 +101,17 @@ static int of_thermal_get_temp(struct thermal_zone_device *tz, return data->ops->get_temp(data->sensor_data, temp); } +static int of_thermal_set_trips(struct thermal_zone_device *tz, + int low, int high) +{ + struct __thermal_zone *data = tz->devdata; + + if (!data->ops || !data->ops->set_trips) + return -EINVAL; + + return data->ops->set_trips(data->sensor_data, low, high); +} + /** * of_thermal_get_ntrips - function to export number of available trip * points. @@ -181,9 +192,6 @@ static int of_thermal_set_emul_temp(struct thermal_zone_device *tz, { struct __thermal_zone *data = tz->devdata; - if (!data->ops || !data->ops->set_emul_temp) - return -EINVAL; - return data->ops->set_emul_temp(data->sensor_data, temp); } @@ -191,25 +199,11 @@ static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip, enum thermal_trend *trend) { struct __thermal_zone *data = tz->devdata; - long dev_trend; - int r; if (!data->ops->get_trend) return -EINVAL; - r = data->ops->get_trend(data->sensor_data, &dev_trend); - if (r) - return r; - - /* TODO: These intervals might have some thresholds, but in core code */ - if (dev_trend > 0) - *trend = THERMAL_TREND_RAISING; - else if (dev_trend < 0) - *trend = THERMAL_TREND_DROPPING; - else - *trend = THERMAL_TREND_STABLE; - - return 0; + return data->ops->get_trend(data->sensor_data, trip, trend); } static int of_thermal_bind(struct thermal_zone_device *thermal, @@ -292,7 +286,7 @@ static int of_thermal_set_mode(struct thermal_zone_device *tz, mutex_unlock(&tz->lock); data->mode = mode; - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); return 0; } @@ -427,7 +421,17 @@ thermal_zone_of_add_sensor(struct device_node *zone, tzd->ops->get_temp = of_thermal_get_temp; tzd->ops->get_trend = of_thermal_get_trend; - tzd->ops->set_emul_temp = of_thermal_set_emul_temp; + + /* + * The thermal zone core will calculate the window if they have set the + * optional set_trips pointer. + */ + if (ops->set_trips) + tzd->ops->set_trips = of_thermal_set_trips; + + if (ops->set_emul_temp) + tzd->ops->set_emul_temp = of_thermal_set_emul_temp; + mutex_unlock(&tzd->lock); return tzd; @@ -596,7 +600,7 @@ static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res, * Return: On success returns a valid struct thermal_zone_device, * otherwise, it returns a corresponding ERR_PTR(). Caller must * check the return value with help of IS_ERR() helper. - * Registered hermal_zone_device device will automatically be + * Registered thermal_zone_device device will automatically be * released when device is unbounded. */ struct thermal_zone_device *devm_thermal_zone_of_sensor_register( diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom-spmi-temp-alarm.c index f8a3c60bef94..819c6d5d7aa7 100644 --- a/drivers/thermal/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom-spmi-temp-alarm.c @@ -150,7 +150,7 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data) { struct qpnp_tm_chip *chip = data; - thermal_zone_device_update(chip->tz_dev); + thermal_zone_device_update(chip->tz_dev, THERMAL_EVENT_UNSPECIFIED); return IRQ_HANDLED; } diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig new file mode 100644 index 000000000000..be32e5abce3c --- /dev/null +++ b/drivers/thermal/qcom/Kconfig @@ -0,0 +1,11 @@ +config QCOM_TSENS + tristate "Qualcomm TSENS Temperature Alarm" + depends on THERMAL + depends on QCOM_QFPROM + depends on ARCH_QCOM || COMPILE_TEST + help + This enables the thermal sysfs driver for the TSENS device. It shows + up in Sysfs as a thermal zone with multiple trip points. Disabling the + thermal zone device via the mode file results in disabling the sensor. + Also able to set threshold temperature for both hot and cold and update + when a threshold is reached. diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile new file mode 100644 index 000000000000..2cc2193637e7 --- /dev/null +++ b/drivers/thermal/qcom/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o +qcom_tsens-y += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-8996.o diff --git a/drivers/thermal/qcom/tsens-8916.c b/drivers/thermal/qcom/tsens-8916.c new file mode 100644 index 000000000000..fdf561b8b81d --- /dev/null +++ b/drivers/thermal/qcom/tsens-8916.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015, The Linux Foundation. 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 and + * only 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. + * + */ + +#include <linux/platform_device.h> +#include "tsens.h" + +/* eeprom layout data for 8916 */ +#define BASE0_MASK 0x0000007f +#define BASE1_MASK 0xfe000000 +#define BASE0_SHIFT 0 +#define BASE1_SHIFT 25 + +#define S0_P1_MASK 0x00000f80 +#define S1_P1_MASK 0x003e0000 +#define S2_P1_MASK 0xf8000000 +#define S3_P1_MASK 0x000003e0 +#define S4_P1_MASK 0x000f8000 + +#define S0_P2_MASK 0x0001f000 +#define S1_P2_MASK 0x07c00000 +#define S2_P2_MASK 0x0000001f +#define S3_P2_MASK 0x00007c00 +#define S4_P2_MASK 0x01f00000 + +#define S0_P1_SHIFT 7 +#define S1_P1_SHIFT 17 +#define S2_P1_SHIFT 27 +#define S3_P1_SHIFT 5 +#define S4_P1_SHIFT 15 + +#define S0_P2_SHIFT 12 +#define S1_P2_SHIFT 22 +#define S2_P2_SHIFT 0 +#define S3_P2_SHIFT 10 +#define S4_P2_SHIFT 20 + +#define CAL_SEL_MASK 0xe0000000 +#define CAL_SEL_SHIFT 29 + +static int calibrate_8916(struct tsens_device *tmdev) +{ + int base0 = 0, base1 = 0, i; + u32 p1[5], p2[5]; + int mode = 0; + u32 *qfprom_cdata, *qfprom_csel; + + qfprom_cdata = (u32 *)qfprom_read(tmdev->dev, "calib"); + if (IS_ERR(qfprom_cdata)) + return PTR_ERR(qfprom_cdata); + + qfprom_csel = (u32 *)qfprom_read(tmdev->dev, "calib_sel"); + if (IS_ERR(qfprom_csel)) + return PTR_ERR(qfprom_csel); + + mode = (qfprom_csel[0] & CAL_SEL_MASK) >> CAL_SEL_SHIFT; + dev_dbg(tmdev->dev, "calibration mode is %d\n", mode); + + switch (mode) { + case TWO_PT_CALIB: + base1 = (qfprom_cdata[1] & BASE1_MASK) >> BASE1_SHIFT; + p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT; + p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT; + p2[2] = (qfprom_cdata[1] & S2_P2_MASK) >> S2_P2_SHIFT; + p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT; + p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT; + for (i = 0; i < tmdev->num_sensors; i++) + p2[i] = ((base1 + p2[i]) << 3); + /* Fall through */ + case ONE_PT_CALIB2: + base0 = (qfprom_cdata[0] & BASE0_MASK); + p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT; + p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT; + p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT; + p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT; + p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT; + for (i = 0; i < tmdev->num_sensors; i++) + p1[i] = (((base0) + p1[i]) << 3); + break; + default: + for (i = 0; i < tmdev->num_sensors; i++) { + p1[i] = 500; + p2[i] = 780; + } + break; + } + + compute_intercept_slope(tmdev, p1, p2, mode); + + return 0; +} + +static const struct tsens_ops ops_8916 = { + .init = init_common, + .calibrate = calibrate_8916, + .get_temp = get_temp_common, +}; + +const struct tsens_data data_8916 = { + .num_sensors = 5, + .ops = &ops_8916, + .hw_ids = (unsigned int []){0, 1, 2, 4, 5 }, +}; diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c new file mode 100644 index 000000000000..0451277d3a8f --- /dev/null +++ b/drivers/thermal/qcom/tsens-8960.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2015, The Linux Foundation. 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 and + * only 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. + * + */ + +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/bitops.h> +#include <linux/regmap.h> +#include <linux/thermal.h> +#include "tsens.h" + +#define CAL_MDEGC 30000 + +#define CONFIG_ADDR 0x3640 +#define CONFIG_ADDR_8660 0x3620 +/* CONFIG_ADDR bitmasks */ +#define CONFIG 0x9b +#define CONFIG_MASK 0xf +#define CONFIG_8660 1 +#define CONFIG_SHIFT_8660 28 +#define CONFIG_MASK_8660 (3 << CONFIG_SHIFT_8660) + +#define STATUS_CNTL_ADDR_8064 0x3660 +#define CNTL_ADDR 0x3620 +/* CNTL_ADDR bitmasks */ +#define EN BIT(0) +#define SW_RST BIT(1) +#define SENSOR0_EN BIT(3) +#define SLP_CLK_ENA BIT(26) +#define SLP_CLK_ENA_8660 BIT(24) +#define MEASURE_PERIOD 1 +#define SENSOR0_SHIFT 3 + +/* INT_STATUS_ADDR bitmasks */ +#define MIN_STATUS_MASK BIT(0) +#define LOWER_STATUS_CLR BIT(1) +#define UPPER_STATUS_CLR BIT(2) +#define MAX_STATUS_MASK BIT(3) + +#define THRESHOLD_ADDR 0x3624 +/* THRESHOLD_ADDR bitmasks */ +#define THRESHOLD_MAX_LIMIT_SHIFT 24 +#define THRESHOLD_MIN_LIMIT_SHIFT 16 +#define THRESHOLD_UPPER_LIMIT_SHIFT 8 +#define THRESHOLD_LOWER_LIMIT_SHIFT 0 + +/* Initial temperature threshold values */ +#define LOWER_LIMIT_TH 0x50 +#define UPPER_LIMIT_TH 0xdf +#define MIN_LIMIT_TH 0x0 +#define MAX_LIMIT_TH 0xff + +#define S0_STATUS_ADDR 0x3628 +#define INT_STATUS_ADDR 0x363c +#define TRDY_MASK BIT(7) +#define TIMEOUT_US 100 + +static int suspend_8960(struct tsens_device *tmdev) +{ + int ret; + unsigned int mask; + struct regmap *map = tmdev->map; + + ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold); + if (ret) + return ret; + + ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control); + if (ret) + return ret; + + if (tmdev->num_sensors > 1) + mask = SLP_CLK_ENA | EN; + else + mask = SLP_CLK_ENA_8660 | EN; + + ret = regmap_update_bits(map, CNTL_ADDR, mask, 0); + if (ret) + return ret; + + return 0; +} + +static int resume_8960(struct tsens_device *tmdev) +{ + int ret; + struct regmap *map = tmdev->map; + + ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST); + if (ret) + return ret; + + /* + * Separate CONFIG restore is not needed only for 8660 as + * config is part of CTRL Addr and its restored as such + */ + if (tmdev->num_sensors > 1) { + ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG); + if (ret) + return ret; + } + + ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold); + if (ret) + return ret; + + ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control); + if (ret) + return ret; + + return 0; +} + +static int enable_8960(struct tsens_device *tmdev, int id) +{ + int ret; + u32 reg, mask; + + ret = regmap_read(tmdev->map, CNTL_ADDR, ®); + if (ret) + return ret; + + mask = BIT(id + SENSOR0_SHIFT); + ret = regmap_write(tmdev->map, CNTL_ADDR, reg | SW_RST); + if (ret) + return ret; + + if (tmdev->num_sensors > 1) + reg |= mask | SLP_CLK_ENA | EN; + else + reg |= mask | SLP_CLK_ENA_8660 | EN; + + ret = regmap_write(tmdev->map, CNTL_ADDR, reg); + if (ret) + return ret; + + return 0; +} + +static void disable_8960(struct tsens_device *tmdev) +{ + int ret; + u32 reg_cntl; + u32 mask; + + mask = GENMASK(tmdev->num_sensors - 1, 0); + mask <<= SENSOR0_SHIFT; + mask |= EN; + + ret = regmap_read(tmdev->map, CNTL_ADDR, ®_cntl); + if (ret) + return; + + reg_cntl &= ~mask; + + if (tmdev->num_sensors > 1) + reg_cntl &= ~SLP_CLK_ENA; + else + reg_cntl &= ~SLP_CLK_ENA_8660; + + regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); +} + +static int init_8960(struct tsens_device *tmdev) +{ + int ret, i; + u32 reg_cntl; + + tmdev->map = dev_get_regmap(tmdev->dev, NULL); + if (!tmdev->map) + return -ENODEV; + + /* + * The status registers for each sensor are discontiguous + * because some SoCs have 5 sensors while others have more + * but the control registers stay in the same place, i.e + * directly after the first 5 status registers. + */ + for (i = 0; i < tmdev->num_sensors; i++) { + if (i >= 5) + tmdev->sensor[i].status = S0_STATUS_ADDR + 40; + tmdev->sensor[i].status += i * 4; + } + + reg_cntl = SW_RST; + ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl); + if (ret) + return ret; + + if (tmdev->num_sensors > 1) { + reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18); + reg_cntl &= ~SW_RST; + ret = regmap_update_bits(tmdev->map, CONFIG_ADDR, + CONFIG_MASK, CONFIG); + } else { + reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16); + reg_cntl &= ~CONFIG_MASK_8660; + reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660; + } + + reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT; + ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); + if (ret) + return ret; + + reg_cntl |= EN; + ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl); + if (ret) + return ret; + + return 0; +} + +static int calibrate_8960(struct tsens_device *tmdev) +{ + int i; + char *data; + + ssize_t num_read = tmdev->num_sensors; + struct tsens_sensor *s = tmdev->sensor; + + data = qfprom_read(tmdev->dev, "calib"); + if (IS_ERR(data)) + data = qfprom_read(tmdev->dev, "calib_backup"); + if (IS_ERR(data)) + return PTR_ERR(data); + + for (i = 0; i < num_read; i++, s++) + s->offset = data[i]; + + return 0; +} + +/* Temperature on y axis and ADC-code on x-axis */ +static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s) +{ + int slope, offset; + + slope = thermal_zone_get_slope(s->tzd); + offset = CAL_MDEGC - slope * s->offset; + + return adc_code * slope + offset; +} + +static int get_temp_8960(struct tsens_device *tmdev, int id, int *temp) +{ + int ret; + u32 code, trdy; + const struct tsens_sensor *s = &tmdev->sensor[id]; + unsigned long timeout; + + timeout = jiffies + usecs_to_jiffies(TIMEOUT_US); + do { + ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy); + if (ret) + return ret; + if (!(trdy & TRDY_MASK)) + continue; + ret = regmap_read(tmdev->map, s->status, &code); + if (ret) + return ret; + *temp = code_to_mdegC(code, s); + return 0; + } while (time_before(jiffies, timeout)); + + return -ETIMEDOUT; +} + +static const struct tsens_ops ops_8960 = { + .init = init_8960, + .calibrate = calibrate_8960, + .get_temp = get_temp_8960, + .enable = enable_8960, + .disable = disable_8960, + .suspend = suspend_8960, + .resume = resume_8960, +}; + +const struct tsens_data data_8960 = { + .num_sensors = 11, + .ops = &ops_8960, +}; diff --git a/drivers/thermal/qcom/tsens-8974.c b/drivers/thermal/qcom/tsens-8974.c new file mode 100644 index 000000000000..9baf77e8cbe3 --- /dev/null +++ b/drivers/thermal/qcom/tsens-8974.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2015, The Linux Foundation. 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 and + * only 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. + * + */ + +#include <linux/platform_device.h> +#include "tsens.h" + +/* eeprom layout data for 8974 */ +#define BASE1_MASK 0xff +#define S0_P1_MASK 0x3f00 +#define S1_P1_MASK 0xfc000 +#define S2_P1_MASK 0x3f00000 +#define S3_P1_MASK 0xfc000000 +#define S4_P1_MASK 0x3f +#define S5_P1_MASK 0xfc0 +#define S6_P1_MASK 0x3f000 +#define S7_P1_MASK 0xfc0000 +#define S8_P1_MASK 0x3f000000 +#define S8_P1_MASK_BKP 0x3f +#define S9_P1_MASK 0x3f +#define S9_P1_MASK_BKP 0xfc0 +#define S10_P1_MASK 0xfc0 +#define S10_P1_MASK_BKP 0x3f000 +#define CAL_SEL_0_1 0xc0000000 +#define CAL_SEL_2 0x40000000 +#define CAL_SEL_SHIFT 30 +#define CAL_SEL_SHIFT_2 28 + +#define S0_P1_SHIFT 8 +#define S1_P1_SHIFT 14 +#define S2_P1_SHIFT 20 +#define S3_P1_SHIFT 26 +#define S5_P1_SHIFT 6 +#define S6_P1_SHIFT 12 +#define S7_P1_SHIFT 18 +#define S8_P1_SHIFT 24 +#define S9_P1_BKP_SHIFT 6 +#define S10_P1_SHIFT 6 +#define S10_P1_BKP_SHIFT 12 + +#define BASE2_SHIFT 12 +#define BASE2_BKP_SHIFT 18 +#define S0_P2_SHIFT 20 +#define S0_P2_BKP_SHIFT 26 +#define S1_P2_SHIFT 26 +#define S2_P2_BKP_SHIFT 6 +#define S3_P2_SHIFT 6 +#define S3_P2_BKP_SHIFT 12 +#define S4_P2_SHIFT 12 +#define S4_P2_BKP_SHIFT 18 +#define S5_P2_SHIFT 18 +#define S5_P2_BKP_SHIFT 24 +#define S6_P2_SHIFT 24 +#define S7_P2_BKP_SHIFT 6 +#define S8_P2_SHIFT 6 +#define S8_P2_BKP_SHIFT 12 +#define S9_P2_SHIFT 12 +#define S9_P2_BKP_SHIFT 18 +#define S10_P2_SHIFT 18 +#define S10_P2_BKP_SHIFT 24 + +#define BASE2_MASK 0xff000 +#define BASE2_BKP_MASK 0xfc0000 +#define S0_P2_MASK 0x3f00000 +#define S0_P2_BKP_MASK 0xfc000000 +#define S1_P2_MASK 0xfc000000 +#define S1_P2_BKP_MASK 0x3f +#define S2_P2_MASK 0x3f +#define S2_P2_BKP_MASK 0xfc0 +#define S3_P2_MASK 0xfc0 +#define S3_P2_BKP_MASK 0x3f000 +#define S4_P2_MASK 0x3f000 +#define S4_P2_BKP_MASK 0xfc0000 +#define S5_P2_MASK 0xfc0000 +#define S5_P2_BKP_MASK 0x3f000000 +#define S6_P2_MASK 0x3f000000 +#define S6_P2_BKP_MASK 0x3f +#define S7_P2_MASK 0x3f +#define S7_P2_BKP_MASK 0xfc0 +#define S8_P2_MASK 0xfc0 +#define S8_P2_BKP_MASK 0x3f000 +#define S9_P2_MASK 0x3f000 +#define S9_P2_BKP_MASK 0xfc0000 +#define S10_P2_MASK 0xfc0000 +#define S10_P2_BKP_MASK 0x3f000000 + +#define BKP_SEL 0x3 +#define BKP_REDUN_SEL 0xe0000000 +#define BKP_REDUN_SHIFT 29 + +#define BIT_APPEND 0x3 + +static int calibrate_8974(struct tsens_device *tmdev) +{ + int base1 = 0, base2 = 0, i; + u32 p1[11], p2[11]; + int mode = 0; + u32 *calib, *bkp; + u32 calib_redun_sel; + + calib = (u32 *)qfprom_read(tmdev->dev, "calib"); + if (IS_ERR(calib)) + return PTR_ERR(calib); + + bkp = (u32 *)qfprom_read(tmdev->dev, "calib_backup"); + if (IS_ERR(bkp)) + return PTR_ERR(bkp); + + calib_redun_sel = bkp[1] & BKP_REDUN_SEL; + calib_redun_sel >>= BKP_REDUN_SHIFT; + + if (calib_redun_sel == BKP_SEL) { + mode = (calib[4] & CAL_SEL_0_1) >> CAL_SEL_SHIFT; + mode |= (calib[5] & CAL_SEL_2) >> CAL_SEL_SHIFT_2; + + switch (mode) { + case TWO_PT_CALIB: + base2 = (bkp[2] & BASE2_BKP_MASK) >> BASE2_BKP_SHIFT; + p2[0] = (bkp[2] & S0_P2_BKP_MASK) >> S0_P2_BKP_SHIFT; + p2[1] = (bkp[3] & S1_P2_BKP_MASK); + p2[2] = (bkp[3] & S2_P2_BKP_MASK) >> S2_P2_BKP_SHIFT; + p2[3] = (bkp[3] & S3_P2_BKP_MASK) >> S3_P2_BKP_SHIFT; + p2[4] = (bkp[3] & S4_P2_BKP_MASK) >> S4_P2_BKP_SHIFT; + p2[5] = (calib[4] & S5_P2_BKP_MASK) >> S5_P2_BKP_SHIFT; + p2[6] = (calib[5] & S6_P2_BKP_MASK); + p2[7] = (calib[5] & S7_P2_BKP_MASK) >> S7_P2_BKP_SHIFT; + p2[8] = (calib[5] & S8_P2_BKP_MASK) >> S8_P2_BKP_SHIFT; + p2[9] = (calib[5] & S9_P2_BKP_MASK) >> S9_P2_BKP_SHIFT; + p2[10] = (calib[5] & S10_P2_BKP_MASK) >> S10_P2_BKP_SHIFT; + /* Fall through */ + case ONE_PT_CALIB: + case ONE_PT_CALIB2: + base1 = bkp[0] & BASE1_MASK; + p1[0] = (bkp[0] & S0_P1_MASK) >> S0_P1_SHIFT; + p1[1] = (bkp[0] & S1_P1_MASK) >> S1_P1_SHIFT; + p1[2] = (bkp[0] & S2_P1_MASK) >> S2_P1_SHIFT; + p1[3] = (bkp[0] & S3_P1_MASK) >> S3_P1_SHIFT; + p1[4] = (bkp[1] & S4_P1_MASK); + p1[5] = (bkp[1] & S5_P1_MASK) >> S5_P1_SHIFT; + p1[6] = (bkp[1] & S6_P1_MASK) >> S6_P1_SHIFT; + p1[7] = (bkp[1] & S7_P1_MASK) >> S7_P1_SHIFT; + p1[8] = (bkp[2] & S8_P1_MASK_BKP) >> S8_P1_SHIFT; + p1[9] = (bkp[2] & S9_P1_MASK_BKP) >> S9_P1_BKP_SHIFT; + p1[10] = (bkp[2] & S10_P1_MASK_BKP) >> S10_P1_BKP_SHIFT; + break; + } + } else { + mode = (calib[1] & CAL_SEL_0_1) >> CAL_SEL_SHIFT; + mode |= (calib[3] & CAL_SEL_2) >> CAL_SEL_SHIFT_2; + + switch (mode) { + case TWO_PT_CALIB: + base2 = (calib[2] & BASE2_MASK) >> BASE2_SHIFT; + p2[0] = (calib[2] & S0_P2_MASK) >> S0_P2_SHIFT; + p2[1] = (calib[2] & S1_P2_MASK) >> S1_P2_SHIFT; + p2[2] = (calib[3] & S2_P2_MASK); + p2[3] = (calib[3] & S3_P2_MASK) >> S3_P2_SHIFT; + p2[4] = (calib[3] & S4_P2_MASK) >> S4_P2_SHIFT; + p2[5] = (calib[3] & S5_P2_MASK) >> S5_P2_SHIFT; + p2[6] = (calib[3] & S6_P2_MASK) >> S6_P2_SHIFT; + p2[7] = (calib[4] & S7_P2_MASK); + p2[8] = (calib[4] & S8_P2_MASK) >> S8_P2_SHIFT; + p2[9] = (calib[4] & S9_P2_MASK) >> S9_P2_SHIFT; + p2[10] = (calib[4] & S10_P2_MASK) >> S10_P2_SHIFT; + /* Fall through */ + case ONE_PT_CALIB: + case ONE_PT_CALIB2: + base1 = calib[0] & BASE1_MASK; + p1[0] = (calib[0] & S0_P1_MASK) >> S0_P1_SHIFT; + p1[1] = (calib[0] & S1_P1_MASK) >> S1_P1_SHIFT; + p1[2] = (calib[0] & S2_P1_MASK) >> S2_P1_SHIFT; + p1[3] = (calib[0] & S3_P1_MASK) >> S3_P1_SHIFT; + p1[4] = (calib[1] & S4_P1_MASK); + p1[5] = (calib[1] & S5_P1_MASK) >> S5_P1_SHIFT; + p1[6] = (calib[1] & S6_P1_MASK) >> S6_P1_SHIFT; + p1[7] = (calib[1] & S7_P1_MASK) >> S7_P1_SHIFT; + p1[8] = (calib[1] & S8_P1_MASK) >> S8_P1_SHIFT; + p1[9] = (calib[2] & S9_P1_MASK); + p1[10] = (calib[2] & S10_P1_MASK) >> S10_P1_SHIFT; + break; + } + } + + switch (mode) { + case ONE_PT_CALIB: + for (i = 0; i < tmdev->num_sensors; i++) + p1[i] += (base1 << 2) | BIT_APPEND; + break; + case TWO_PT_CALIB: + for (i = 0; i < tmdev->num_sensors; i++) { + p2[i] += base2; + p2[i] <<= 2; + p2[i] |= BIT_APPEND; + } + /* Fall through */ + case ONE_PT_CALIB2: + for (i = 0; i < tmdev->num_sensors; i++) { + p1[i] += base1; + p1[i] <<= 2; + p1[i] |= BIT_APPEND; + } + break; + default: + for (i = 0; i < tmdev->num_sensors; i++) + p2[i] = 780; + p1[0] = 502; + p1[1] = 509; + p1[2] = 503; + p1[3] = 509; + p1[4] = 505; + p1[5] = 509; + p1[6] = 507; + p1[7] = 510; + p1[8] = 508; + p1[9] = 509; + p1[10] = 508; + break; + } + + compute_intercept_slope(tmdev, p1, p2, mode); + + return 0; +} + +static const struct tsens_ops ops_8974 = { + .init = init_common, + .calibrate = calibrate_8974, + .get_temp = get_temp_common, +}; + +const struct tsens_data data_8974 = { + .num_sensors = 11, + .ops = &ops_8974, +}; diff --git a/drivers/thermal/qcom/tsens-8996.c b/drivers/thermal/qcom/tsens-8996.c new file mode 100644 index 000000000000..e1f77818d8fa --- /dev/null +++ b/drivers/thermal/qcom/tsens-8996.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2015, The Linux Foundation. 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 and + * only 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. + * + */ + +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include "tsens.h" + +#define STATUS_OFFSET 0x10a0 +#define LAST_TEMP_MASK 0xfff +#define STATUS_VALID_BIT BIT(21) +#define CODE_SIGN_BIT BIT(11) + +static int get_temp_8996(struct tsens_device *tmdev, int id, int *temp) +{ + struct tsens_sensor *s = &tmdev->sensor[id]; + u32 code; + unsigned int sensor_addr; + int last_temp = 0, last_temp2 = 0, last_temp3 = 0, ret; + + sensor_addr = STATUS_OFFSET + s->hw_id * 4; + ret = regmap_read(tmdev->map, sensor_addr, &code); + if (ret) + return ret; + last_temp = code & LAST_TEMP_MASK; + if (code & STATUS_VALID_BIT) + goto done; + + /* Try a second time */ + ret = regmap_read(tmdev->map, sensor_addr, &code); + if (ret) + return ret; + if (code & STATUS_VALID_BIT) { + last_temp = code & LAST_TEMP_MASK; + goto done; + } else { + last_temp2 = code & LAST_TEMP_MASK; + } + + /* Try a third/last time */ + ret = regmap_read(tmdev->map, sensor_addr, &code); + if (ret) + return ret; + if (code & STATUS_VALID_BIT) { + last_temp = code & LAST_TEMP_MASK; + goto done; + } else { + last_temp3 = code & LAST_TEMP_MASK; + } + + if (last_temp == last_temp2) + last_temp = last_temp2; + else if (last_temp2 == last_temp3) + last_temp = last_temp3; +done: + /* Code sign bit is the sign extension for a negative value */ + if (last_temp & CODE_SIGN_BIT) + last_temp |= ~CODE_SIGN_BIT; + + /* Temperatures are in deciCelicius */ + *temp = last_temp * 100; + + return 0; +} + +static const struct tsens_ops ops_8996 = { + .init = init_common, + .get_temp = get_temp_8996, +}; + +const struct tsens_data data_8996 = { + .num_sensors = 13, + .ops = &ops_8996, +}; diff --git a/drivers/thermal/qcom/tsens-common.c b/drivers/thermal/qcom/tsens-common.c new file mode 100644 index 000000000000..b1449ad67fc0 --- /dev/null +++ b/drivers/thermal/qcom/tsens-common.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015, The Linux Foundation. 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 and + * only 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. + * + */ + +#include <linux/err.h> +#include <linux/io.h> +#include <linux/nvmem-consumer.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include "tsens.h" + +#define S0_ST_ADDR 0x1030 +#define SN_ADDR_OFFSET 0x4 +#define SN_ST_TEMP_MASK 0x3ff +#define CAL_DEGC_PT1 30 +#define CAL_DEGC_PT2 120 +#define SLOPE_FACTOR 1000 +#define SLOPE_DEFAULT 3200 + +char *qfprom_read(struct device *dev, const char *cname) +{ + struct nvmem_cell *cell; + ssize_t data; + char *ret; + + cell = nvmem_cell_get(dev, cname); + if (IS_ERR(cell)) + return ERR_CAST(cell); + + ret = nvmem_cell_read(cell, &data); + nvmem_cell_put(cell); + + return ret; +} + +/* + * Use this function on devices where slope and offset calculations + * depend on calibration data read from qfprom. On others the slope + * and offset values are derived from tz->tzp->slope and tz->tzp->offset + * resp. + */ +void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1, + u32 *p2, u32 mode) +{ + int i; + int num, den; + + for (i = 0; i < tmdev->num_sensors; i++) { + dev_dbg(tmdev->dev, + "sensor%d - data_point1:%#x data_point2:%#x\n", + i, p1[i], p2[i]); + + tmdev->sensor[i].slope = SLOPE_DEFAULT; + if (mode == TWO_PT_CALIB) { + /* + * slope (m) = adc_code2 - adc_code1 (y2 - y1)/ + * temp_120_degc - temp_30_degc (x2 - x1) + */ + num = p2[i] - p1[i]; + num *= SLOPE_FACTOR; + den = CAL_DEGC_PT2 - CAL_DEGC_PT1; + tmdev->sensor[i].slope = num / den; + } + + tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) - + (CAL_DEGC_PT1 * + tmdev->sensor[i].slope); + dev_dbg(tmdev->dev, "offset:%d\n", tmdev->sensor[i].offset); + } +} + +static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s) +{ + int degc, num, den; + + num = (adc_code * SLOPE_FACTOR) - s->offset; + den = s->slope; + + if (num > 0) + degc = num + (den / 2); + else if (num < 0) + degc = num - (den / 2); + else + degc = num; + + degc /= den; + + return degc; +} + +int get_temp_common(struct tsens_device *tmdev, int id, int *temp) +{ + struct tsens_sensor *s = &tmdev->sensor[id]; + u32 code; + unsigned int sensor_addr; + int last_temp = 0, ret; + + sensor_addr = S0_ST_ADDR + s->hw_id * SN_ADDR_OFFSET; + ret = regmap_read(tmdev->map, sensor_addr, &code); + if (ret) + return ret; + last_temp = code & SN_ST_TEMP_MASK; + + *temp = code_to_degc(last_temp, s) * 1000; + + return 0; +} + +static const struct regmap_config tsens_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +int __init init_common(struct tsens_device *tmdev) +{ + void __iomem *base; + + base = of_iomap(tmdev->dev->of_node, 0); + if (!base) + return -EINVAL; + + tmdev->map = devm_regmap_init_mmio(tmdev->dev, base, &tsens_config); + if (IS_ERR(tmdev->map)) { + iounmap(base); + return PTR_ERR(tmdev->map); + } + + return 0; +} diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c new file mode 100644 index 000000000000..3f9fe6aa51cc --- /dev/null +++ b/drivers/thermal/qcom/tsens.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2015, The Linux Foundation. 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 and + * only 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. + * + */ + +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <linux/thermal.h> +#include "tsens.h" + +static int tsens_get_temp(void *data, int *temp) +{ + const struct tsens_sensor *s = data; + struct tsens_device *tmdev = s->tmdev; + + return tmdev->ops->get_temp(tmdev, s->id, temp); +} + +static int tsens_get_trend(void *p, int trip, enum thermal_trend *trend) +{ + const struct tsens_sensor *s = p; + struct tsens_device *tmdev = s->tmdev; + + if (tmdev->ops->get_trend) + return tmdev->ops->get_trend(tmdev, s->id, trend); + + return -ENOTSUPP; +} + +static int __maybe_unused tsens_suspend(struct device *dev) +{ + struct tsens_device *tmdev = dev_get_drvdata(dev); + + if (tmdev->ops && tmdev->ops->suspend) + return tmdev->ops->suspend(tmdev); + + return 0; +} + +static int __maybe_unused tsens_resume(struct device *dev) +{ + struct tsens_device *tmdev = dev_get_drvdata(dev); + + if (tmdev->ops && tmdev->ops->resume) + return tmdev->ops->resume(tmdev); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); + +static const struct of_device_id tsens_table[] = { + { + .compatible = "qcom,msm8916-tsens", + .data = &data_8916, + }, { + .compatible = "qcom,msm8974-tsens", + .data = &data_8974, + }, { + .compatible = "qcom,msm8996-tsens", + .data = &data_8996, + }, + {} +}; +MODULE_DEVICE_TABLE(of, tsens_table); + +static const struct thermal_zone_of_device_ops tsens_of_ops = { + .get_temp = tsens_get_temp, + .get_trend = tsens_get_trend, +}; + +static int tsens_register(struct tsens_device *tmdev) +{ + int i; + struct thermal_zone_device *tzd; + u32 *hw_id, n = tmdev->num_sensors; + + hw_id = devm_kcalloc(tmdev->dev, n, sizeof(u32), GFP_KERNEL); + if (!hw_id) + return -ENOMEM; + + for (i = 0; i < tmdev->num_sensors; i++) { + tmdev->sensor[i].tmdev = tmdev; + tmdev->sensor[i].id = i; + tzd = devm_thermal_zone_of_sensor_register(tmdev->dev, i, + &tmdev->sensor[i], + &tsens_of_ops); + if (IS_ERR(tzd)) + continue; + tmdev->sensor[i].tzd = tzd; + if (tmdev->ops->enable) + tmdev->ops->enable(tmdev, i); + } + return 0; +} + +static int tsens_probe(struct platform_device *pdev) +{ + int ret, i; + struct device *dev; + struct device_node *np; + struct tsens_sensor *s; + struct tsens_device *tmdev; + const struct tsens_data *data; + const struct of_device_id *id; + + if (pdev->dev.of_node) + dev = &pdev->dev; + else + dev = pdev->dev.parent; + + np = dev->of_node; + + id = of_match_node(tsens_table, np); + if (id) + data = id->data; + else + data = &data_8960; + + if (data->num_sensors <= 0) { + dev_err(dev, "invalid number of sensors\n"); + return -EINVAL; + } + + tmdev = devm_kzalloc(dev, sizeof(*tmdev) + + data->num_sensors * sizeof(*s), GFP_KERNEL); + if (!tmdev) + return -ENOMEM; + + tmdev->dev = dev; + tmdev->num_sensors = data->num_sensors; + tmdev->ops = data->ops; + for (i = 0; i < tmdev->num_sensors; i++) { + if (data->hw_ids) + tmdev->sensor[i].hw_id = data->hw_ids[i]; + else + tmdev->sensor[i].hw_id = i; + } + + if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp) + return -EINVAL; + + ret = tmdev->ops->init(tmdev); + if (ret < 0) { + dev_err(dev, "tsens init failed\n"); + return ret; + } + + if (tmdev->ops->calibrate) { + ret = tmdev->ops->calibrate(tmdev); + if (ret < 0) { + dev_err(dev, "tsens calibration failed\n"); + return ret; + } + } + + ret = tsens_register(tmdev); + + platform_set_drvdata(pdev, tmdev); + + return ret; +} + +static int tsens_remove(struct platform_device *pdev) +{ + struct tsens_device *tmdev = platform_get_drvdata(pdev); + + if (tmdev->ops->disable) + tmdev->ops->disable(tmdev); + + return 0; +} + +static struct platform_driver tsens_driver = { + .probe = tsens_probe, + .remove = tsens_remove, + .driver = { + .name = "qcom-tsens", + .pm = &tsens_pm_ops, + .of_match_table = tsens_table, + }, +}; +module_platform_driver(tsens_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("QCOM Temperature Sensor driver"); +MODULE_ALIAS("platform:qcom-tsens"); diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h new file mode 100644 index 000000000000..911c1978892b --- /dev/null +++ b/drivers/thermal/qcom/tsens.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#ifndef __QCOM_TSENS_H__ +#define __QCOM_TSENS_H__ + +#define ONE_PT_CALIB 0x1 +#define ONE_PT_CALIB2 0x2 +#define TWO_PT_CALIB 0x3 + +#include <linux/thermal.h> + +struct tsens_device; + +struct tsens_sensor { + struct tsens_device *tmdev; + struct thermal_zone_device *tzd; + int offset; + int id; + int hw_id; + int slope; + u32 status; +}; + +/** + * struct tsens_ops - operations as supported by the tsens device + * @init: Function to initialize the tsens device + * @calibrate: Function to calibrate the tsens device + * @get_temp: Function which returns the temp in millidegC + * @enable: Function to enable (clocks/power) tsens device + * @disable: Function to disable the tsens device + * @suspend: Function to suspend the tsens device + * @resume: Function to resume the tsens device + * @get_trend: Function to get the thermal/temp trend + */ +struct tsens_ops { + /* mandatory callbacks */ + int (*init)(struct tsens_device *); + int (*calibrate)(struct tsens_device *); + int (*get_temp)(struct tsens_device *, int, int *); + /* optional callbacks */ + int (*enable)(struct tsens_device *, int); + void (*disable)(struct tsens_device *); + int (*suspend)(struct tsens_device *); + int (*resume)(struct tsens_device *); + int (*get_trend)(struct tsens_device *, int, enum thermal_trend *); +}; + +/** + * struct tsens_data - tsens instance specific data + * @num_sensors: Max number of sensors supported by platform + * @ops: operations the tsens instance supports + * @hw_ids: Subset of sensors ids supported by platform, if not the first n + */ +struct tsens_data { + const u32 num_sensors; + const struct tsens_ops *ops; + unsigned int *hw_ids; +}; + +/* Registers to be saved/restored across a context loss */ +struct tsens_context { + int threshold; + int control; +}; + +struct tsens_device { + struct device *dev; + u32 num_sensors; + struct regmap *map; + struct regmap_field *status_field; + struct tsens_context ctx; + bool trdy; + const struct tsens_ops *ops; + struct tsens_sensor sensor[0]; +}; + +char *qfprom_read(struct device *, const char *); +void compute_intercept_slope(struct tsens_device *, u32 *, u32 *, u32); +int init_common(struct tsens_device *); +int get_temp_common(struct tsens_device *, int, int *); + +extern const struct tsens_data data_8916, data_8974, data_8960, data_8996; + +#endif /* __QCOM_TSENS_H__ */ diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c new file mode 100644 index 000000000000..644ba526d9ea --- /dev/null +++ b/drivers/thermal/qoriq_thermal.c @@ -0,0 +1,328 @@ +/* + * Copyright 2016 Freescale Semiconductor, Inc. + * + * 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/module.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/thermal.h> + +#include "thermal_core.h" + +#define SITES_MAX 16 + +/* + * QorIQ TMU Registers + */ +struct qoriq_tmu_site_regs { + u32 tritsr; /* Immediate Temperature Site Register */ + u32 tratsr; /* Average Temperature Site Register */ + u8 res0[0x8]; +}; + +struct qoriq_tmu_regs { + u32 tmr; /* Mode Register */ +#define TMR_DISABLE 0x0 +#define TMR_ME 0x80000000 +#define TMR_ALPF 0x0c000000 + u32 tsr; /* Status Register */ + u32 tmtmir; /* Temperature measurement interval Register */ +#define TMTMIR_DEFAULT 0x0000000f + u8 res0[0x14]; + u32 tier; /* Interrupt Enable Register */ +#define TIER_DISABLE 0x0 + u32 tidr; /* Interrupt Detect Register */ + u32 tiscr; /* Interrupt Site Capture Register */ + u32 ticscr; /* Interrupt Critical Site Capture Register */ + u8 res1[0x10]; + u32 tmhtcrh; /* High Temperature Capture Register */ + u32 tmhtcrl; /* Low Temperature Capture Register */ + u8 res2[0x8]; + u32 tmhtitr; /* High Temperature Immediate Threshold */ + u32 tmhtatr; /* High Temperature Average Threshold */ + u32 tmhtactr; /* High Temperature Average Crit Threshold */ + u8 res3[0x24]; + u32 ttcfgr; /* Temperature Configuration Register */ + u32 tscfgr; /* Sensor Configuration Register */ + u8 res4[0x78]; + struct qoriq_tmu_site_regs site[SITES_MAX]; + u8 res5[0x9f8]; + u32 ipbrr0; /* IP Block Revision Register 0 */ + u32 ipbrr1; /* IP Block Revision Register 1 */ + u8 res6[0x310]; + u32 ttr0cr; /* Temperature Range 0 Control Register */ + u32 ttr1cr; /* Temperature Range 1 Control Register */ + u32 ttr2cr; /* Temperature Range 2 Control Register */ + u32 ttr3cr; /* Temperature Range 3 Control Register */ +}; + +/* + * Thermal zone data + */ +struct qoriq_tmu_data { + struct thermal_zone_device *tz; + struct qoriq_tmu_regs __iomem *regs; + int sensor_id; + bool little_endian; +}; + +static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr) +{ + if (p->little_endian) + iowrite32(val, addr); + else + iowrite32be(val, addr); +} + +static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr) +{ + if (p->little_endian) + return ioread32(addr); + else + return ioread32be(addr); +} + +static int tmu_get_temp(void *p, int *temp) +{ + u32 val; + struct qoriq_tmu_data *data = p; + + val = tmu_read(data, &data->regs->site[data->sensor_id].tritsr); + *temp = (val & 0xff) * 1000; + + return 0; +} + +static int qoriq_tmu_get_sensor_id(void) +{ + int ret, id; + struct of_phandle_args sensor_specs; + struct device_node *np, *sensor_np; + + np = of_find_node_by_name(NULL, "thermal-zones"); + if (!np) + return -ENODEV; + + sensor_np = of_get_next_child(np, NULL); + ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors", + "#thermal-sensor-cells", + 0, &sensor_specs); + if (ret) { + of_node_put(np); + of_node_put(sensor_np); + return ret; + } + + if (sensor_specs.args_count >= 1) { + id = sensor_specs.args[0]; + WARN(sensor_specs.args_count > 1, + "%s: too many cells in sensor specifier %d\n", + sensor_specs.np->name, sensor_specs.args_count); + } else { + id = 0; + } + + of_node_put(np); + of_node_put(sensor_np); + + return id; +} + +static int qoriq_tmu_calibration(struct platform_device *pdev) +{ + int i, val, len; + u32 range[4]; + const u32 *calibration; + struct device_node *np = pdev->dev.of_node; + struct qoriq_tmu_data *data = platform_get_drvdata(pdev); + + if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) { + dev_err(&pdev->dev, "missing calibration range.\n"); + return -ENODEV; + } + + /* Init temperature range registers */ + tmu_write(data, range[0], &data->regs->ttr0cr); + tmu_write(data, range[1], &data->regs->ttr1cr); + tmu_write(data, range[2], &data->regs->ttr2cr); + tmu_write(data, range[3], &data->regs->ttr3cr); + + calibration = of_get_property(np, "fsl,tmu-calibration", &len); + if (calibration == NULL || len % 8) { + dev_err(&pdev->dev, "invalid calibration data.\n"); + return -ENODEV; + } + + for (i = 0; i < len; i += 8, calibration += 2) { + val = of_read_number(calibration, 1); + tmu_write(data, val, &data->regs->ttcfgr); + val = of_read_number(calibration + 1, 1); + tmu_write(data, val, &data->regs->tscfgr); + } + + return 0; +} + +static void qoriq_tmu_init_device(struct qoriq_tmu_data *data) +{ + /* Disable interrupt, using polling instead */ + tmu_write(data, TIER_DISABLE, &data->regs->tier); + + /* Set update_interval */ + tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir); + + /* Disable monitoring */ + tmu_write(data, TMR_DISABLE, &data->regs->tmr); +} + +static struct thermal_zone_of_device_ops tmu_tz_ops = { + .get_temp = tmu_get_temp, +}; + +static int qoriq_tmu_probe(struct platform_device *pdev) +{ + int ret; + const struct thermal_trip *trip; + struct qoriq_tmu_data *data; + struct device_node *np = pdev->dev.of_node; + u32 site = 0; + + if (!np) { + dev_err(&pdev->dev, "Device OF-Node is NULL"); + return -ENODEV; + } + + data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + + data->little_endian = of_property_read_bool(np, "little-endian"); + + data->sensor_id = qoriq_tmu_get_sensor_id(); + if (data->sensor_id < 0) { + dev_err(&pdev->dev, "Failed to get sensor id\n"); + ret = -ENODEV; + goto err_iomap; + } + + data->regs = of_iomap(np, 0); + if (!data->regs) { + dev_err(&pdev->dev, "Failed to get memory region\n"); + ret = -ENODEV; + goto err_iomap; + } + + qoriq_tmu_init_device(data); /* TMU initialization */ + + ret = qoriq_tmu_calibration(pdev); /* TMU calibration */ + if (ret < 0) + goto err_tmu; + + data->tz = thermal_zone_of_sensor_register(&pdev->dev, data->sensor_id, + data, &tmu_tz_ops); + if (IS_ERR(data->tz)) { + ret = PTR_ERR(data->tz); + dev_err(&pdev->dev, + "Failed to register thermal zone device %d\n", ret); + goto err_tmu; + } + + trip = of_thermal_get_trip_points(data->tz); + + /* Enable monitoring */ + site |= 0x1 << (15 - data->sensor_id); + tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr); + + return 0; + +err_tmu: + iounmap(data->regs); + +err_iomap: + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int qoriq_tmu_remove(struct platform_device *pdev) +{ + struct qoriq_tmu_data *data = platform_get_drvdata(pdev); + + thermal_zone_of_sensor_unregister(&pdev->dev, data->tz); + + /* Disable monitoring */ + tmu_write(data, TMR_DISABLE, &data->regs->tmr); + + iounmap(data->regs); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int qoriq_tmu_suspend(struct device *dev) +{ + u32 tmr; + struct qoriq_tmu_data *data = dev_get_drvdata(dev); + + /* Disable monitoring */ + tmr = tmu_read(data, &data->regs->tmr); + tmr &= ~TMR_ME; + tmu_write(data, tmr, &data->regs->tmr); + + return 0; +} + +static int qoriq_tmu_resume(struct device *dev) +{ + u32 tmr; + struct qoriq_tmu_data *data = dev_get_drvdata(dev); + + /* Enable monitoring */ + tmr = tmu_read(data, &data->regs->tmr); + tmr |= TMR_ME; + tmu_write(data, tmr, &data->regs->tmr); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops, + qoriq_tmu_suspend, qoriq_tmu_resume); + +static const struct of_device_id qoriq_tmu_match[] = { + { .compatible = "fsl,qoriq-tmu", }, + {}, +}; +MODULE_DEVICE_TABLE(of, qoriq_tmu_match); + +static struct platform_driver qoriq_tmu = { + .driver = { + .name = "qoriq_thermal", + .pm = &qoriq_tmu_pm_ops, + .of_match_table = qoriq_tmu_match, + }, + .probe = qoriq_tmu_probe, + .remove = qoriq_tmu_remove, +}; +module_platform_driver(qoriq_tmu); + +MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>"); +MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 5f817923f374..73e5fee6cf1d 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -31,6 +31,8 @@ #include <linux/spinlock.h> #include <linux/thermal.h> +#include "thermal_hwmon.h" + #define IDLE_INTERVAL 5000 #define COMMON_STR 0x00 @@ -75,6 +77,8 @@ struct rcar_thermal_priv { #define rcar_priv_to_dev(priv) ((priv)->common->dev) #define rcar_has_irq_support(priv) ((priv)->common->base) #define rcar_id_to_shift(priv) ((priv)->id * 8) +#define rcar_of_data(dev) ((unsigned long)of_device_get_match_data(dev)) +#define rcar_use_of_thermal(dev) (rcar_of_data(dev) == USE_OF_THERMAL) #define USE_OF_THERMAL 1 static const struct of_device_id rcar_thermal_dt_ids[] = { @@ -354,7 +358,8 @@ static void rcar_thermal_work(struct work_struct *work) return; if (nctemp != cctemp) - thermal_zone_device_update(priv->zone); + thermal_zone_device_update(priv->zone, + THERMAL_EVENT_UNSPECIFIED); } static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status) @@ -415,7 +420,10 @@ static int rcar_thermal_remove(struct platform_device *pdev) rcar_thermal_for_each_priv(priv, common) { rcar_thermal_irq_disable(priv); - thermal_zone_device_unregister(priv->zone); + if (rcar_use_of_thermal(dev)) + thermal_remove_hwmon_sysfs(priv->zone); + else + thermal_zone_device_unregister(priv->zone); } pm_runtime_put(dev); @@ -430,7 +438,6 @@ static int rcar_thermal_probe(struct platform_device *pdev) struct rcar_thermal_priv *priv; struct device *dev = &pdev->dev; struct resource *res, *irq; - unsigned long of_data = (unsigned long)of_device_get_match_data(dev); int mres = 0; int i; int ret = -ENODEV; @@ -491,7 +498,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) if (ret < 0) goto error_unregister; - if (of_data == USE_OF_THERMAL) + if (rcar_use_of_thermal(dev)) priv->zone = devm_thermal_zone_of_sensor_register( dev, i, priv, &rcar_thermal_zone_of_ops); @@ -508,6 +515,17 @@ static int rcar_thermal_probe(struct platform_device *pdev) goto error_unregister; } + if (rcar_use_of_thermal(dev)) { + /* + * thermal_zone doesn't enable hwmon as default, + * but, enable it here to keep compatible + */ + priv->zone->tzp->no_hwmon = false; + ret = thermal_add_hwmon_sysfs(priv->zone); + if (ret) + goto error_unregister; + } + rcar_thermal_irq_enable(priv); list_move_tail(&priv->list, &common->head); diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index 5d491f16a866..e227a9f0acf7 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -96,6 +96,7 @@ struct chip_tsadc_table { * @initialize: SoC special initialize tsadc controller method * @irq_ack: clear the interrupt * @get_temp: get the temperature + * @set_alarm_temp: set the high temperature interrupt * @set_tshut_temp: set the hardware-controlled shutdown temperature * @set_tshut_mode: set the hardware-controlled shutdown mode * @table: the chip-specific conversion table @@ -119,6 +120,8 @@ struct rockchip_tsadc_chip { /* Per-sensor methods */ int (*get_temp)(struct chip_tsadc_table table, int chn, void __iomem *reg, int *temp); + void (*set_alarm_temp)(struct chip_tsadc_table table, + int chn, void __iomem *reg, int temp); void (*set_tshut_temp)(struct chip_tsadc_table table, int chn, void __iomem *reg, int temp); void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m); @@ -183,6 +186,7 @@ struct rockchip_thermal_data { #define TSADCV2_INT_EN 0x08 #define TSADCV2_INT_PD 0x0c #define TSADCV2_DATA(chn) (0x20 + (chn) * 0x04) +#define TSADCV2_COMP_INT(chn) (0x30 + (chn) * 0x04) #define TSADCV2_COMP_SHUT(chn) (0x40 + (chn) * 0x04) #define TSADCV2_HIGHT_INT_DEBOUNCE 0x60 #define TSADCV2_HIGHT_TSHUT_DEBOUNCE 0x64 @@ -207,18 +211,21 @@ struct rockchip_thermal_data { #define TSADCV2_HIGHT_INT_DEBOUNCE_COUNT 4 #define TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT 4 -#define TSADCV2_AUTO_PERIOD_TIME 250 /* msec */ -#define TSADCV2_AUTO_PERIOD_HT_TIME 50 /* msec */ +#define TSADCV2_AUTO_PERIOD_TIME 250 /* 250ms */ +#define TSADCV2_AUTO_PERIOD_HT_TIME 50 /* 50ms */ +#define TSADCV3_AUTO_PERIOD_TIME 1875 /* 2.5ms */ +#define TSADCV3_AUTO_PERIOD_HT_TIME 1875 /* 2.5ms */ + #define TSADCV2_USER_INTER_PD_SOC 0x340 /* 13 clocks */ #define GRF_SARADC_TESTBIT 0x0e644 #define GRF_TSADC_TESTBIT_L 0x0e648 #define GRF_TSADC_TESTBIT_H 0x0e64c -#define GRF_TSADC_TSEN_PD_ON (0x30003 << 0) -#define GRF_TSADC_TSEN_PD_OFF (0x30000 << 0) #define GRF_SARADC_TESTBIT_ON (0x10001 << 2) #define GRF_TSADC_TESTBIT_H_ON (0x10001 << 2) +#define GRF_TSADC_VCM_EN_L (0x10001 << 7) +#define GRF_TSADC_VCM_EN_H (0x10001 << 7) /** * struct tsadc_table - code to temperature conversion table @@ -394,13 +401,17 @@ static u32 rk_tsadcv2_temp_to_code(struct chip_tsadc_table table, int temp) { int high, low, mid; + u32 error = 0; low = 0; high = table.length - 1; mid = (high + low) / 2; - if (temp < table.id[low].temp || temp > table.id[high].temp) - return 0; + /* Return mask code data when the temp is over table range */ + if (temp < table.id[low].temp || temp > table.id[high].temp) { + error = table.data_mask; + goto exit; + } while (low <= high) { if (temp == table.id[mid].temp) @@ -412,7 +423,9 @@ static u32 rk_tsadcv2_temp_to_code(struct chip_tsadc_table table, mid = (low + high) / 2; } - return 0; +exit: + pr_err("Invalid the conversion, error=%d\n", error); + return error; } static int rk_tsadcv2_code_to_temp(struct chip_tsadc_table table, u32 code, @@ -543,14 +556,34 @@ static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs, /* Set interleave value to workround ic time sync issue */ writel_relaxed(TSADCV2_USER_INTER_PD_SOC, regs + TSADCV2_USER_CON); + + writel_relaxed(TSADCV2_AUTO_PERIOD_TIME, + regs + TSADCV2_AUTO_PERIOD); + writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT, + regs + TSADCV2_HIGHT_INT_DEBOUNCE); + writel_relaxed(TSADCV2_AUTO_PERIOD_HT_TIME, + regs + TSADCV2_AUTO_PERIOD_HT); + writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT, + regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE); + } else { - regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_ON); - mdelay(10); - regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_OFF); + /* Enable the voltage common mode feature */ + regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_VCM_EN_L); + regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_VCM_EN_H); + usleep_range(15, 100); /* The spec note says at least 15 us */ regmap_write(grf, GRF_SARADC_TESTBIT, GRF_SARADC_TESTBIT_ON); regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_TESTBIT_H_ON); usleep_range(90, 200); /* The spec note says at least 90 us */ + + writel_relaxed(TSADCV3_AUTO_PERIOD_TIME, + regs + TSADCV2_AUTO_PERIOD); + writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT, + regs + TSADCV2_HIGHT_INT_DEBOUNCE); + writel_relaxed(TSADCV3_AUTO_PERIOD_HT_TIME, + regs + TSADCV2_AUTO_PERIOD_HT); + writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT, + regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE); } if (tshut_polarity == TSHUT_HIGH_ACTIVE) @@ -559,14 +592,6 @@ static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs, else writel_relaxed(0U & ~TSADCV2_AUTO_TSHUT_POLARITY_HIGH, regs + TSADCV2_AUTO_CON); - - writel_relaxed(TSADCV2_AUTO_PERIOD_TIME, regs + TSADCV2_AUTO_PERIOD); - writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT, - regs + TSADCV2_HIGHT_INT_DEBOUNCE); - writel_relaxed(TSADCV2_AUTO_PERIOD_HT_TIME, - regs + TSADCV2_AUTO_PERIOD_HT); - writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT, - regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE); } static void rk_tsadcv2_irq_ack(void __iomem *regs) @@ -628,12 +653,34 @@ static int rk_tsadcv2_get_temp(struct chip_tsadc_table table, return rk_tsadcv2_code_to_temp(table, val, temp); } +static void rk_tsadcv2_alarm_temp(struct chip_tsadc_table table, + int chn, void __iomem *regs, int temp) +{ + u32 alarm_value, int_en; + + /* Make sure the value is valid */ + alarm_value = rk_tsadcv2_temp_to_code(table, temp); + if (alarm_value == table.data_mask) + return; + + writel_relaxed(alarm_value & table.data_mask, + regs + TSADCV2_COMP_INT(chn)); + + int_en = readl_relaxed(regs + TSADCV2_INT_EN); + int_en |= TSADCV2_INT_SRC_EN(chn); + writel_relaxed(int_en, regs + TSADCV2_INT_EN); +} + static void rk_tsadcv2_tshut_temp(struct chip_tsadc_table table, int chn, void __iomem *regs, int temp) { u32 tshut_value, val; + /* Make sure the value is valid */ tshut_value = rk_tsadcv2_temp_to_code(table, temp); + if (tshut_value == table.data_mask) + return; + writel_relaxed(tshut_value, regs + TSADCV2_COMP_SHUT(chn)); /* TSHUT will be valid */ @@ -670,6 +717,7 @@ static const struct rockchip_tsadc_chip rk3228_tsadc_data = { .irq_ack = rk_tsadcv3_irq_ack, .control = rk_tsadcv3_control, .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, .set_tshut_temp = rk_tsadcv2_tshut_temp, .set_tshut_mode = rk_tsadcv2_tshut_mode, @@ -694,6 +742,7 @@ static const struct rockchip_tsadc_chip rk3288_tsadc_data = { .irq_ack = rk_tsadcv2_irq_ack, .control = rk_tsadcv2_control, .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, .set_tshut_temp = rk_tsadcv2_tshut_temp, .set_tshut_mode = rk_tsadcv2_tshut_mode, @@ -718,6 +767,7 @@ static const struct rockchip_tsadc_chip rk3366_tsadc_data = { .irq_ack = rk_tsadcv3_irq_ack, .control = rk_tsadcv3_control, .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, .set_tshut_temp = rk_tsadcv2_tshut_temp, .set_tshut_mode = rk_tsadcv2_tshut_mode, @@ -742,6 +792,7 @@ static const struct rockchip_tsadc_chip rk3368_tsadc_data = { .irq_ack = rk_tsadcv2_irq_ack, .control = rk_tsadcv2_control, .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, .set_tshut_temp = rk_tsadcv2_tshut_temp, .set_tshut_mode = rk_tsadcv2_tshut_mode, @@ -766,6 +817,7 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = { .irq_ack = rk_tsadcv3_irq_ack, .control = rk_tsadcv3_control, .get_temp = rk_tsadcv2_get_temp, + .set_alarm_temp = rk_tsadcv2_alarm_temp, .set_tshut_temp = rk_tsadcv2_tshut_temp, .set_tshut_mode = rk_tsadcv2_tshut_mode, @@ -821,11 +873,27 @@ static irqreturn_t rockchip_thermal_alarm_irq_thread(int irq, void *dev) thermal->chip->irq_ack(thermal->regs); for (i = 0; i < thermal->chip->chn_num; i++) - thermal_zone_device_update(thermal->sensors[i].tzd); + thermal_zone_device_update(thermal->sensors[i].tzd, + THERMAL_EVENT_UNSPECIFIED); return IRQ_HANDLED; } +static int rockchip_thermal_set_trips(void *_sensor, int low, int high) +{ + struct rockchip_thermal_sensor *sensor = _sensor; + struct rockchip_thermal_data *thermal = sensor->thermal; + const struct rockchip_tsadc_chip *tsadc = thermal->chip; + + dev_dbg(&thermal->pdev->dev, "%s: sensor %d: low: %d, high %d\n", + __func__, sensor->id, low, high); + + tsadc->set_alarm_temp(tsadc->table, + sensor->id, thermal->regs, high); + + return 0; +} + static int rockchip_thermal_get_temp(void *_sensor, int *out_temp) { struct rockchip_thermal_sensor *sensor = _sensor; @@ -843,6 +911,7 @@ static int rockchip_thermal_get_temp(void *_sensor, int *out_temp) static const struct thermal_zone_of_device_ops rockchip_of_thermal_ops = { .get_temp = rockchip_thermal_get_temp, + .set_trips = rockchip_thermal_set_trips, }; static int rockchip_configure_from_dt(struct device *dev, diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index f3ce94ec73b5..ad1186dd6132 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -225,7 +225,7 @@ static void exynos_report_trigger(struct exynos_tmu_data *p) return; } - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); mutex_lock(&tz->lock); /* Find the level for which trip happened */ diff --git a/drivers/thermal/st/st_thermal_memmap.c b/drivers/thermal/st/st_thermal_memmap.c index fc0c9e198710..91d42319de27 100644 --- a/drivers/thermal/st/st_thermal_memmap.c +++ b/drivers/thermal/st/st_thermal_memmap.c @@ -42,7 +42,8 @@ static irqreturn_t st_mmap_thermal_trip_handler(int irq, void *sdata) { struct st_thermal_sensor *sensor = sdata; - thermal_zone_device_update(sensor->thermal_dev); + thermal_zone_device_update(sensor->thermal_dev, + THERMAL_EVENT_UNSPECIFIED); return IRQ_HANDLED; } diff --git a/drivers/thermal/tango_thermal.c b/drivers/thermal/tango_thermal.c index 70e0d9f406e9..201304aeafeb 100644 --- a/drivers/thermal/tango_thermal.c +++ b/drivers/thermal/tango_thermal.c @@ -64,6 +64,12 @@ static const struct thermal_zone_of_device_ops ops = { .get_temp = tango_get_temp, }; +static void tango_thermal_init(struct tango_thermal_priv *priv) +{ + writel(0, priv->base + TEMPSI_CFG); + writel(CMD_ON, priv->base + TEMPSI_CMD); +} + static int tango_thermal_probe(struct platform_device *pdev) { struct resource *res; @@ -79,14 +85,22 @@ static int tango_thermal_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); + platform_set_drvdata(pdev, priv); priv->thresh_idx = IDX_MIN; - writel(0, priv->base + TEMPSI_CFG); - writel(CMD_ON, priv->base + TEMPSI_CMD); + tango_thermal_init(priv); tzdev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv, &ops); return PTR_ERR_OR_ZERO(tzdev); } +static int __maybe_unused tango_thermal_resume(struct device *dev) +{ + tango_thermal_init(dev_get_drvdata(dev)); + return 0; +} + +static SIMPLE_DEV_PM_OPS(tango_thermal_pm, NULL, tango_thermal_resume); + static const struct of_device_id tango_sensor_ids[] = { { .compatible = "sigma,smp8758-thermal", @@ -99,6 +113,7 @@ static struct platform_driver tango_thermal_driver = { .driver = { .name = "tango-thermal", .of_match_table = tango_sensor_ids, + .pm = &tango_thermal_pm, }, }; diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index b8651726201e..7d2db23d71a3 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -30,6 +30,7 @@ #include <dt-bindings/thermal/tegra124-soctherm.h> +#include "../thermal_core.h" #include "soctherm.h" #define SENSOR_CONFIG0 0 @@ -67,35 +68,228 @@ #define READBACK_ADD_HALF BIT(7) #define READBACK_NEGATE BIT(0) +/* + * THERMCTL_LEVEL0_GROUP_CPU is defined in soctherm.h + * because it will be used by tegraxxx_soctherm.c + */ +#define THERMCTL_LVL0_CPU0_EN_MASK BIT(8) +#define THERMCTL_LVL0_CPU0_CPU_THROT_MASK (0x3 << 5) +#define THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT 0x1 +#define THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY 0x2 +#define THERMCTL_LVL0_CPU0_GPU_THROT_MASK (0x3 << 3) +#define THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT 0x1 +#define THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY 0x2 +#define THERMCTL_LVL0_CPU0_MEM_THROT_MASK BIT(2) +#define THERMCTL_LVL0_CPU0_STATUS_MASK 0x3 + +#define THERMCTL_LVL0_UP_STATS 0x10 +#define THERMCTL_LVL0_DN_STATS 0x14 + +#define THERMCTL_STATS_CTL 0x94 +#define STATS_CTL_CLR_DN 0x8 +#define STATS_CTL_EN_DN 0x4 +#define STATS_CTL_CLR_UP 0x2 +#define STATS_CTL_EN_UP 0x1 + +#define THROT_GLOBAL_CFG 0x400 +#define THROT_GLOBAL_ENB_MASK BIT(0) + +#define CPU_PSKIP_STATUS 0x418 +#define XPU_PSKIP_STATUS_M_MASK (0xff << 12) +#define XPU_PSKIP_STATUS_N_MASK (0xff << 4) +#define XPU_PSKIP_STATUS_SW_OVERRIDE_MASK BIT(1) +#define XPU_PSKIP_STATUS_ENABLED_MASK BIT(0) + +#define THROT_PRIORITY_LOCK 0x424 +#define THROT_PRIORITY_LOCK_PRIORITY_MASK 0xff + +#define THROT_STATUS 0x428 +#define THROT_STATUS_BREACH_MASK BIT(12) +#define THROT_STATUS_STATE_MASK (0xff << 4) +#define THROT_STATUS_ENABLED_MASK BIT(0) + +#define THROT_PSKIP_CTRL_LITE_CPU 0x430 +#define THROT_PSKIP_CTRL_ENABLE_MASK BIT(31) +#define THROT_PSKIP_CTRL_DIVIDEND_MASK (0xff << 8) +#define THROT_PSKIP_CTRL_DIVISOR_MASK 0xff +#define THROT_PSKIP_CTRL_VECT_GPU_MASK (0x7 << 16) +#define THROT_PSKIP_CTRL_VECT_CPU_MASK (0x7 << 8) +#define THROT_PSKIP_CTRL_VECT2_CPU_MASK 0x7 + +#define THROT_VECT_NONE 0x0 /* 3'b000 */ +#define THROT_VECT_LOW 0x1 /* 3'b001 */ +#define THROT_VECT_MED 0x3 /* 3'b011 */ +#define THROT_VECT_HIGH 0x7 /* 3'b111 */ + +#define THROT_PSKIP_RAMP_LITE_CPU 0x434 +#define THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK BIT(31) +#define THROT_PSKIP_RAMP_DURATION_MASK (0xffff << 8) +#define THROT_PSKIP_RAMP_STEP_MASK 0xff + +#define THROT_PRIORITY_LITE 0x444 +#define THROT_PRIORITY_LITE_PRIO_MASK 0xff + +#define THROT_DELAY_LITE 0x448 +#define THROT_DELAY_LITE_DELAY_MASK 0xff + +/* car register offsets needed for enabling HW throttling */ +#define CAR_SUPER_CCLKG_DIVIDER 0x36c +#define CDIVG_USE_THERM_CONTROLS_MASK BIT(30) + +/* ccroc register offsets needed for enabling HW throttling for Tegra132 */ +#define CCROC_SUPER_CCLKG_DIVIDER 0x024 + +#define CCROC_GLOBAL_CFG 0x148 + +#define CCROC_THROT_PSKIP_RAMP_CPU 0x150 +#define CCROC_THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK BIT(31) +#define CCROC_THROT_PSKIP_RAMP_DURATION_MASK (0xffff << 8) +#define CCROC_THROT_PSKIP_RAMP_STEP_MASK 0xff + +#define CCROC_THROT_PSKIP_CTRL_CPU 0x154 +#define CCROC_THROT_PSKIP_CTRL_ENB_MASK BIT(31) +#define CCROC_THROT_PSKIP_CTRL_DIVIDEND_MASK (0xff << 8) +#define CCROC_THROT_PSKIP_CTRL_DIVISOR_MASK 0xff + /* get val from register(r) mask bits(m) */ #define REG_GET_MASK(r, m) (((r) & (m)) >> (ffs(m) - 1)) /* set val(v) to mask bits(m) of register(r) */ #define REG_SET_MASK(r, m, v) (((r) & ~(m)) | \ (((v) & (m >> (ffs(m) - 1))) << (ffs(m) - 1))) +/* get dividend from the depth */ +#define THROT_DEPTH_DIVIDEND(depth) ((256 * (100 - (depth)) / 100) - 1) + +/* get THROT_PSKIP_xxx offset per LIGHT/HEAVY throt and CPU/GPU dev */ +#define THROT_OFFSET 0x30 +#define THROT_PSKIP_CTRL(throt, dev) (THROT_PSKIP_CTRL_LITE_CPU + \ + (THROT_OFFSET * throt) + (8 * dev)) +#define THROT_PSKIP_RAMP(throt, dev) (THROT_PSKIP_RAMP_LITE_CPU + \ + (THROT_OFFSET * throt) + (8 * dev)) + +/* get THROT_xxx_CTRL offset per LIGHT/HEAVY throt */ +#define THROT_PRIORITY_CTRL(throt) (THROT_PRIORITY_LITE + \ + (THROT_OFFSET * throt)) +#define THROT_DELAY_CTRL(throt) (THROT_DELAY_LITE + \ + (THROT_OFFSET * throt)) + +/* get CCROC_THROT_PSKIP_xxx offset per HIGH/MED/LOW vect*/ +#define CCROC_THROT_OFFSET 0x0c +#define CCROC_THROT_PSKIP_CTRL_CPU_REG(vect) (CCROC_THROT_PSKIP_CTRL_CPU + \ + (CCROC_THROT_OFFSET * vect)) +#define CCROC_THROT_PSKIP_RAMP_CPU_REG(vect) (CCROC_THROT_PSKIP_RAMP_CPU + \ + (CCROC_THROT_OFFSET * vect)) + +/* get THERMCTL_LEVELx offset per CPU/GPU/MEM/TSENSE rg and LEVEL0~3 lv */ +#define THERMCTL_LVL_REGS_SIZE 0x20 +#define THERMCTL_LVL_REG(rg, lv) ((rg) + ((lv) * THERMCTL_LVL_REGS_SIZE)) + static const int min_low_temp = -127000; static const int max_high_temp = 127000; +enum soctherm_throttle_id { + THROTTLE_LIGHT = 0, + THROTTLE_HEAVY, + THROTTLE_SIZE, +}; + +enum soctherm_throttle_dev_id { + THROTTLE_DEV_CPU = 0, + THROTTLE_DEV_GPU, + THROTTLE_DEV_SIZE, +}; + +static const char *const throt_names[] = { + [THROTTLE_LIGHT] = "light", + [THROTTLE_HEAVY] = "heavy", +}; + +struct tegra_soctherm; struct tegra_thermctl_zone { void __iomem *reg; struct device *dev; + struct tegra_soctherm *ts; struct thermal_zone_device *tz; const struct tegra_tsensor_group *sg; }; +struct soctherm_throt_cfg { + const char *name; + unsigned int id; + u8 priority; + u8 cpu_throt_level; + u32 cpu_throt_depth; + struct thermal_cooling_device *cdev; + bool init; +}; + struct tegra_soctherm { struct reset_control *reset; struct clk *clock_tsensor; struct clk *clock_soctherm; void __iomem *regs; - struct thermal_zone_device **thermctl_tzs; + void __iomem *clk_regs; + void __iomem *ccroc_regs; u32 *calib; + struct thermal_zone_device **thermctl_tzs; struct tegra_soctherm_soc *soc; + struct soctherm_throt_cfg throt_cfgs[THROTTLE_SIZE]; + struct dentry *debugfs_dir; }; +/** + * clk_writel() - writes a value to a CAR register + * @ts: pointer to a struct tegra_soctherm + * @v: the value to write + * @reg: the register offset + * + * Writes @v to @reg. No return value. + */ +static inline void clk_writel(struct tegra_soctherm *ts, u32 value, u32 reg) +{ + writel(value, (ts->clk_regs + reg)); +} + +/** + * clk_readl() - reads specified register from CAR IP block + * @ts: pointer to a struct tegra_soctherm + * @reg: register address to be read + * + * Return: the value of the register + */ +static inline u32 clk_readl(struct tegra_soctherm *ts, u32 reg) +{ + return readl(ts->clk_regs + reg); +} + +/** + * ccroc_writel() - writes a value to a CCROC register + * @ts: pointer to a struct tegra_soctherm + * @v: the value to write + * @reg: the register offset + * + * Writes @v to @reg. No return value. + */ +static inline void ccroc_writel(struct tegra_soctherm *ts, u32 value, u32 reg) +{ + writel(value, (ts->ccroc_regs + reg)); +} + +/** + * ccroc_readl() - reads specified register from CCROC IP block + * @ts: pointer to a struct tegra_soctherm + * @reg: register address to be read + * + * Return: the value of the register + */ +static inline u32 ccroc_readl(struct tegra_soctherm *ts, u32 reg) +{ + return readl(ts->ccroc_regs + reg); +} + static void enable_tsensor(struct tegra_soctherm *tegra, unsigned int i) { const struct tegra_tsensor *sensor = &tegra->soc->tsensors[i]; @@ -150,11 +344,17 @@ static int tegra_thermctl_get_temp(void *data, int *out_temp) static int thermtrip_program(struct device *dev, const struct tegra_tsensor_group *sg, int trip_temp); +static int +throttrip_program(struct device *dev, const struct tegra_tsensor_group *sg, + struct soctherm_throt_cfg *stc, int trip_temp); +static struct soctherm_throt_cfg * +find_throttle_cfg_by_name(struct tegra_soctherm *ts, const char *name); static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp) { struct tegra_thermctl_zone *zone = data; struct thermal_zone_device *tz = zone->tz; + struct tegra_soctherm *ts = zone->ts; const struct tegra_tsensor_group *sg = zone->sg; struct device *dev = zone->dev; enum thermal_trip_type type; @@ -167,10 +367,29 @@ static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp) if (ret) return ret; - if (type != THERMAL_TRIP_CRITICAL) - return 0; + if (type == THERMAL_TRIP_CRITICAL) { + return thermtrip_program(dev, sg, temp); + } else if (type == THERMAL_TRIP_HOT) { + int i; + + for (i = 0; i < THROTTLE_SIZE; i++) { + struct thermal_cooling_device *cdev; + struct soctherm_throt_cfg *stc; + + if (!ts->throt_cfgs[i].init) + continue; + + cdev = ts->throt_cfgs[i].cdev; + if (get_thermal_instance(tz, cdev, trip)) + stc = find_throttle_cfg_by_name(ts, cdev->type); + else + continue; + + return throttrip_program(dev, sg, stc, temp); + } + } - return thermtrip_program(dev, sg, temp); + return 0; } static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = { @@ -238,14 +457,110 @@ static int thermtrip_program(struct device *dev, } /** + * throttrip_program() - Configures the hardware to throttle the + * pulse if a given sensor group reaches a given temperature + * @dev: ptr to the struct device for the SOC_THERM IP block + * @sg: pointer to the sensor group to set the thermtrip temperature for + * @stc: pointer to the throttle need to be triggered + * @trip_temp: the temperature in millicelsius to trigger the thermal trip at + * + * Sets the thermal trip threshold and throttle event of the given sensor + * group. If this threshold is crossed, the hardware will trigger the + * throttle. + * + * Note that, although @trip_temp is specified in millicelsius, the + * hardware is programmed in degrees Celsius. + * + * Return: 0 upon success, or %-EINVAL upon failure. + */ +static int throttrip_program(struct device *dev, + const struct tegra_tsensor_group *sg, + struct soctherm_throt_cfg *stc, + int trip_temp) +{ + struct tegra_soctherm *ts = dev_get_drvdata(dev); + int temp, cpu_throt, gpu_throt; + unsigned int throt; + u32 r, reg_off; + + if (!dev || !sg || !stc || !stc->init) + return -EINVAL; + + temp = enforce_temp_range(dev, trip_temp) / ts->soc->thresh_grain; + + /* Hardcode LIGHT on LEVEL1 and HEAVY on LEVEL2 */ + throt = stc->id; + reg_off = THERMCTL_LVL_REG(sg->thermctl_lvl0_offset, throt + 1); + + if (throt == THROTTLE_LIGHT) { + cpu_throt = THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT; + gpu_throt = THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT; + } else { + cpu_throt = THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY; + gpu_throt = THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY; + if (throt != THROTTLE_HEAVY) + dev_warn(dev, + "invalid throt id %d - assuming HEAVY", + throt); + } + + r = readl(ts->regs + reg_off); + r = REG_SET_MASK(r, sg->thermctl_lvl0_up_thresh_mask, temp); + r = REG_SET_MASK(r, sg->thermctl_lvl0_dn_thresh_mask, temp); + r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_CPU_THROT_MASK, cpu_throt); + r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_GPU_THROT_MASK, gpu_throt); + r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 1); + writel(r, ts->regs + reg_off); + + return 0; +} + +static struct soctherm_throt_cfg * +find_throttle_cfg_by_name(struct tegra_soctherm *ts, const char *name) +{ + unsigned int i; + + for (i = 0; ts->throt_cfgs[i].name; i++) + if (!strcmp(ts->throt_cfgs[i].name, name)) + return &ts->throt_cfgs[i]; + + return NULL; +} + +static int get_hot_temp(struct thermal_zone_device *tz, int *trip, int *temp) +{ + int ntrips, i, ret; + enum thermal_trip_type type; + + ntrips = of_thermal_get_ntrips(tz); + if (ntrips <= 0) + return -EINVAL; + + for (i = 0; i < ntrips; i++) { + ret = tz->ops->get_trip_type(tz, i, &type); + if (ret) + return -EINVAL; + if (type == THERMAL_TRIP_HOT) { + ret = tz->ops->get_trip_temp(tz, i, temp); + if (!ret) + *trip = i; + + return ret; + } + } + + return -EINVAL; +} + +/** * tegra_soctherm_set_hwtrips() - set HW trip point from DT data * @dev: struct device * of the SOC_THERM instance * * Configure the SOC_THERM HW trip points, setting "THERMTRIP" - * trip points , using "critical" type trip_temp from thermal - * zone. - * After they have been configured, THERMTRIP will take action - * when the configured SoC thermal sensor group reaches a + * "THROTTLE" trip points , using "critical" or "hot" type trip_temp + * from thermal zone. + * After they have been configured, THERMTRIP or THROTTLE will take + * action when the configured SoC thermal sensor group reaches a * certain temperature. * * Return: 0 upon success, or a negative error code on failure. @@ -254,19 +569,24 @@ static int thermtrip_program(struct device *dev, * THERMTRIP has been enabled successfully when a message similar to * this one appears on the serial console: * "thermtrip: will shut down when sensor group XXX reaches YYYYYY mC" + * THROTTLE has been enabled successfully when a message similar to + * this one appears on the serial console: + * ""throttrip: will throttle when sensor group XXX reaches YYYYYY mC" */ static int tegra_soctherm_set_hwtrips(struct device *dev, const struct tegra_tsensor_group *sg, struct thermal_zone_device *tz) { - int temperature; + struct tegra_soctherm *ts = dev_get_drvdata(dev); + struct soctherm_throt_cfg *stc; + int i, trip, temperature; int ret; ret = tz->ops->get_crit_temp(tz, &temperature); if (ret) { dev_warn(dev, "thermtrip: %s: missing critical temperature\n", sg->name); - return ret; + goto set_throttle; } ret = thermtrip_program(dev, sg, temperature); @@ -280,6 +600,43 @@ static int tegra_soctherm_set_hwtrips(struct device *dev, "thermtrip: will shut down when %s reaches %d mC\n", sg->name, temperature); +set_throttle: + ret = get_hot_temp(tz, &trip, &temperature); + if (ret) { + dev_warn(dev, "throttrip: %s: missing hot temperature\n", + sg->name); + return 0; + } + + for (i = 0; i < THROTTLE_SIZE; i++) { + struct thermal_cooling_device *cdev; + + if (!ts->throt_cfgs[i].init) + continue; + + cdev = ts->throt_cfgs[i].cdev; + if (get_thermal_instance(tz, cdev, trip)) + stc = find_throttle_cfg_by_name(ts, cdev->type); + else + continue; + + ret = throttrip_program(dev, sg, stc, temperature); + if (ret) { + dev_err(dev, "throttrip: %s: error during enable\n", + sg->name); + return ret; + } + + dev_info(dev, + "throttrip: will throttle when %s reaches %d mC\n", + sg->name, temperature); + break; + } + + if (i == THROTTLE_SIZE) + dev_warn(dev, "throttrip: %s: missing throttle cdev\n", + sg->name); + return 0; } @@ -291,7 +648,7 @@ static int regs_show(struct seq_file *s, void *data) const struct tegra_tsensor *tsensors = ts->soc->tsensors; const struct tegra_tsensor_group **ttgs = ts->soc->ttgs; u32 r, state; - int i; + int i, level; seq_puts(s, "-----TSENSE (convert HW)-----\n"); @@ -365,6 +722,81 @@ static int regs_show(struct seq_file *s, void *data) state = REG_GET_MASK(r, SENSOR_TEMP2_MEM_TEMP_MASK); seq_printf(s, " MEM(%d)\n", translate_temp(state)); + for (i = 0; i < ts->soc->num_ttgs; i++) { + seq_printf(s, "%s:\n", ttgs[i]->name); + for (level = 0; level < 4; level++) { + s32 v; + u32 mask; + u16 off = ttgs[i]->thermctl_lvl0_offset; + + r = readl(ts->regs + THERMCTL_LVL_REG(off, level)); + + mask = ttgs[i]->thermctl_lvl0_up_thresh_mask; + state = REG_GET_MASK(r, mask); + v = sign_extend32(state, ts->soc->bptt - 1); + v *= ts->soc->thresh_grain; + seq_printf(s, " %d: Up/Dn(%d /", level, v); + + mask = ttgs[i]->thermctl_lvl0_dn_thresh_mask; + state = REG_GET_MASK(r, mask); + v = sign_extend32(state, ts->soc->bptt - 1); + v *= ts->soc->thresh_grain; + seq_printf(s, "%d ) ", v); + + mask = THERMCTL_LVL0_CPU0_EN_MASK; + state = REG_GET_MASK(r, mask); + seq_printf(s, "En(%d) ", state); + + mask = THERMCTL_LVL0_CPU0_CPU_THROT_MASK; + state = REG_GET_MASK(r, mask); + seq_puts(s, "CPU Throt"); + if (!state) + seq_printf(s, "(%s) ", "none"); + else if (state == THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT) + seq_printf(s, "(%s) ", "L"); + else if (state == THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY) + seq_printf(s, "(%s) ", "H"); + else + seq_printf(s, "(%s) ", "H+L"); + + mask = THERMCTL_LVL0_CPU0_GPU_THROT_MASK; + state = REG_GET_MASK(r, mask); + seq_puts(s, "GPU Throt"); + if (!state) + seq_printf(s, "(%s) ", "none"); + else if (state == THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT) + seq_printf(s, "(%s) ", "L"); + else if (state == THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY) + seq_printf(s, "(%s) ", "H"); + else + seq_printf(s, "(%s) ", "H+L"); + + mask = THERMCTL_LVL0_CPU0_STATUS_MASK; + state = REG_GET_MASK(r, mask); + seq_printf(s, "Status(%s)\n", + state == 0 ? "LO" : + state == 1 ? "In" : + state == 2 ? "Res" : "HI"); + } + } + + r = readl(ts->regs + THERMCTL_STATS_CTL); + seq_printf(s, "STATS: Up(%s) Dn(%s)\n", + r & STATS_CTL_EN_UP ? "En" : "--", + r & STATS_CTL_EN_DN ? "En" : "--"); + + for (level = 0; level < 4; level++) { + u16 off; + + off = THERMCTL_LVL0_UP_STATS; + r = readl(ts->regs + THERMCTL_LVL_REG(off, level)); + seq_printf(s, " Level_%d Up(%d) ", level, r); + + off = THERMCTL_LVL0_DN_STATS; + r = readl(ts->regs + THERMCTL_LVL_REG(off, level)); + seq_printf(s, "Dn(%d)\n", r); + } + r = readl(ts->regs + THERMCTL_THERMTRIP_CTL); state = REG_GET_MASK(r, ttgs[0]->thermtrip_any_en_mask); seq_printf(s, "Thermtrip Any En(%d)\n", state); @@ -376,6 +808,32 @@ static int regs_show(struct seq_file *s, void *data) seq_printf(s, "Thresh(%d)\n", state); } + r = readl(ts->regs + THROT_GLOBAL_CFG); + seq_puts(s, "\n"); + seq_printf(s, "GLOBAL THROTTLE CONFIG: 0x%08x\n", r); + + seq_puts(s, "---------------------------------------------------\n"); + r = readl(ts->regs + THROT_STATUS); + state = REG_GET_MASK(r, THROT_STATUS_BREACH_MASK); + seq_printf(s, "THROT STATUS: breach(%d) ", state); + state = REG_GET_MASK(r, THROT_STATUS_STATE_MASK); + seq_printf(s, "state(%d) ", state); + state = REG_GET_MASK(r, THROT_STATUS_ENABLED_MASK); + seq_printf(s, "enabled(%d)\n", state); + + r = readl(ts->regs + CPU_PSKIP_STATUS); + if (ts->soc->use_ccroc) { + state = REG_GET_MASK(r, XPU_PSKIP_STATUS_ENABLED_MASK); + seq_printf(s, "CPU PSKIP STATUS: enabled(%d)\n", state); + } else { + state = REG_GET_MASK(r, XPU_PSKIP_STATUS_M_MASK); + seq_printf(s, "CPU PSKIP STATUS: M(%d) ", state); + state = REG_GET_MASK(r, XPU_PSKIP_STATUS_N_MASK); + seq_printf(s, "N(%d) ", state); + state = REG_GET_MASK(r, XPU_PSKIP_STATUS_ENABLED_MASK); + seq_printf(s, "enabled(%d)\n", state); + } + return 0; } @@ -449,6 +907,326 @@ static int soctherm_clk_enable(struct platform_device *pdev, bool enable) return 0; } +static int throt_get_cdev_max_state(struct thermal_cooling_device *cdev, + unsigned long *max_state) +{ + *max_state = 1; + return 0; +} + +static int throt_get_cdev_cur_state(struct thermal_cooling_device *cdev, + unsigned long *cur_state) +{ + struct tegra_soctherm *ts = cdev->devdata; + u32 r; + + r = readl(ts->regs + THROT_STATUS); + if (REG_GET_MASK(r, THROT_STATUS_STATE_MASK)) + *cur_state = 1; + else + *cur_state = 0; + + return 0; +} + +static int throt_set_cdev_state(struct thermal_cooling_device *cdev, + unsigned long cur_state) +{ + return 0; +} + +static struct thermal_cooling_device_ops throt_cooling_ops = { + .get_max_state = throt_get_cdev_max_state, + .get_cur_state = throt_get_cdev_cur_state, + .set_cur_state = throt_set_cdev_state, +}; + +/** + * soctherm_init_hw_throt_cdev() - Parse the HW throttle configurations + * and register them as cooling devices. + */ +static void soctherm_init_hw_throt_cdev(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tegra_soctherm *ts = dev_get_drvdata(dev); + struct device_node *np_stc, *np_stcc; + const char *name; + u32 val; + int i, r; + + for (i = 0; i < THROTTLE_SIZE; i++) { + ts->throt_cfgs[i].name = throt_names[i]; + ts->throt_cfgs[i].id = i; + ts->throt_cfgs[i].init = false; + } + + np_stc = of_get_child_by_name(dev->of_node, "throttle-cfgs"); + if (!np_stc) { + dev_info(dev, + "throttle-cfg: no throttle-cfgs - not enabling\n"); + return; + } + + for_each_child_of_node(np_stc, np_stcc) { + struct soctherm_throt_cfg *stc; + struct thermal_cooling_device *tcd; + + name = np_stcc->name; + stc = find_throttle_cfg_by_name(ts, name); + if (!stc) { + dev_err(dev, + "throttle-cfg: could not find %s\n", name); + continue; + } + + r = of_property_read_u32(np_stcc, "nvidia,priority", &val); + if (r) { + dev_info(dev, + "throttle-cfg: %s: missing priority\n", name); + continue; + } + stc->priority = val; + + if (ts->soc->use_ccroc) { + r = of_property_read_u32(np_stcc, + "nvidia,cpu-throt-level", + &val); + if (r) { + dev_info(dev, + "throttle-cfg: %s: missing cpu-throt-level\n", + name); + continue; + } + stc->cpu_throt_level = val; + } else { + r = of_property_read_u32(np_stcc, + "nvidia,cpu-throt-percent", + &val); + if (r) { + dev_info(dev, + "throttle-cfg: %s: missing cpu-throt-percent\n", + name); + continue; + } + stc->cpu_throt_depth = val; + } + + tcd = thermal_of_cooling_device_register(np_stcc, + (char *)name, ts, + &throt_cooling_ops); + of_node_put(np_stcc); + if (IS_ERR_OR_NULL(tcd)) { + dev_err(dev, + "throttle-cfg: %s: failed to register cooling device\n", + name); + continue; + } + + stc->cdev = tcd; + stc->init = true; + } + + of_node_put(np_stc); +} + +/** + * throttlectl_cpu_level_cfg() - programs CCROC NV_THERM level config + * @level: describing the level LOW/MED/HIGH of throttling + * + * It's necessary to set up the CPU-local CCROC NV_THERM instance with + * the M/N values desired for each level. This function does this. + * + * This function pre-programs the CCROC NV_THERM levels in terms of + * pre-configured "Low", "Medium" or "Heavy" throttle levels which are + * mapped to THROT_LEVEL_LOW, THROT_LEVEL_MED and THROT_LEVEL_HVY. + */ +static void throttlectl_cpu_level_cfg(struct tegra_soctherm *ts, int level) +{ + u8 depth, dividend; + u32 r; + + switch (level) { + case TEGRA_SOCTHERM_THROT_LEVEL_LOW: + depth = 50; + break; + case TEGRA_SOCTHERM_THROT_LEVEL_MED: + depth = 75; + break; + case TEGRA_SOCTHERM_THROT_LEVEL_HIGH: + depth = 80; + break; + case TEGRA_SOCTHERM_THROT_LEVEL_NONE: + return; + default: + return; + } + + dividend = THROT_DEPTH_DIVIDEND(depth); + + /* setup PSKIP in ccroc nv_therm registers */ + r = ccroc_readl(ts, CCROC_THROT_PSKIP_RAMP_CPU_REG(level)); + r = REG_SET_MASK(r, CCROC_THROT_PSKIP_RAMP_DURATION_MASK, 0xff); + r = REG_SET_MASK(r, CCROC_THROT_PSKIP_RAMP_STEP_MASK, 0xf); + ccroc_writel(ts, r, CCROC_THROT_PSKIP_RAMP_CPU_REG(level)); + + r = ccroc_readl(ts, CCROC_THROT_PSKIP_CTRL_CPU_REG(level)); + r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_ENB_MASK, 1); + r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_DIVIDEND_MASK, dividend); + r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_DIVISOR_MASK, 0xff); + ccroc_writel(ts, r, CCROC_THROT_PSKIP_CTRL_CPU_REG(level)); +} + +/** + * throttlectl_cpu_level_select() - program CPU pulse skipper config + * @throt: the LIGHT/HEAVY of throttle event id + * + * Pulse skippers are used to throttle clock frequencies. This + * function programs the pulse skippers based on @throt and platform + * data. This function is used on SoCs which have CPU-local pulse + * skipper control, such as T13x. It programs soctherm's interface to + * Denver:CCROC NV_THERM in terms of Low, Medium and HIGH throttling + * vectors. PSKIP_BYPASS mode is set as required per HW spec. + */ +static void throttlectl_cpu_level_select(struct tegra_soctherm *ts, + enum soctherm_throttle_id throt) +{ + u32 r, throt_vect; + + /* Denver:CCROC NV_THERM interface N:3 Mapping */ + switch (ts->throt_cfgs[throt].cpu_throt_level) { + case TEGRA_SOCTHERM_THROT_LEVEL_LOW: + throt_vect = THROT_VECT_LOW; + break; + case TEGRA_SOCTHERM_THROT_LEVEL_MED: + throt_vect = THROT_VECT_MED; + break; + case TEGRA_SOCTHERM_THROT_LEVEL_HIGH: + throt_vect = THROT_VECT_HIGH; + break; + default: + throt_vect = THROT_VECT_NONE; + break; + } + + r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU)); + r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1); + r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT_CPU_MASK, throt_vect); + r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT2_CPU_MASK, throt_vect); + writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU)); + + /* bypass sequencer in soc_therm as it is programmed in ccroc */ + r = REG_SET_MASK(0, THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK, 1); + writel(r, ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU)); +} + +/** + * throttlectl_cpu_mn() - program CPU pulse skipper configuration + * @throt: the LIGHT/HEAVY of throttle event id + * + * Pulse skippers are used to throttle clock frequencies. This + * function programs the pulse skippers based on @throt and platform + * data. This function is used for CPUs that have "remote" pulse + * skipper control, e.g., the CPU pulse skipper is controlled by the + * SOC_THERM IP block. (SOC_THERM is located outside the CPU + * complex.) + */ +static void throttlectl_cpu_mn(struct tegra_soctherm *ts, + enum soctherm_throttle_id throt) +{ + u32 r; + int depth; + u8 dividend; + + depth = ts->throt_cfgs[throt].cpu_throt_depth; + dividend = THROT_DEPTH_DIVIDEND(depth); + + r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU)); + r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1); + r = REG_SET_MASK(r, THROT_PSKIP_CTRL_DIVIDEND_MASK, dividend); + r = REG_SET_MASK(r, THROT_PSKIP_CTRL_DIVISOR_MASK, 0xff); + writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU)); + + r = readl(ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU)); + r = REG_SET_MASK(r, THROT_PSKIP_RAMP_DURATION_MASK, 0xff); + r = REG_SET_MASK(r, THROT_PSKIP_RAMP_STEP_MASK, 0xf); + writel(r, ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU)); +} + +/** + * soctherm_throttle_program() - programs pulse skippers' configuration + * @throt: the LIGHT/HEAVY of the throttle event id. + * + * Pulse skippers are used to throttle clock frequencies. + * This function programs the pulse skippers. + */ +static void soctherm_throttle_program(struct tegra_soctherm *ts, + enum soctherm_throttle_id throt) +{ + u32 r; + struct soctherm_throt_cfg stc = ts->throt_cfgs[throt]; + + if (!stc.init) + return; + + /* Setup PSKIP parameters */ + if (ts->soc->use_ccroc) + throttlectl_cpu_level_select(ts, throt); + else + throttlectl_cpu_mn(ts, throt); + + r = REG_SET_MASK(0, THROT_PRIORITY_LITE_PRIO_MASK, stc.priority); + writel(r, ts->regs + THROT_PRIORITY_CTRL(throt)); + + r = REG_SET_MASK(0, THROT_DELAY_LITE_DELAY_MASK, 0); + writel(r, ts->regs + THROT_DELAY_CTRL(throt)); + + r = readl(ts->regs + THROT_PRIORITY_LOCK); + r = REG_GET_MASK(r, THROT_PRIORITY_LOCK_PRIORITY_MASK); + if (r >= stc.priority) + return; + r = REG_SET_MASK(0, THROT_PRIORITY_LOCK_PRIORITY_MASK, + stc.priority); + writel(r, ts->regs + THROT_PRIORITY_LOCK); +} + +static void tegra_soctherm_throttle(struct device *dev) +{ + struct tegra_soctherm *ts = dev_get_drvdata(dev); + u32 v; + int i; + + /* configure LOW, MED and HIGH levels for CCROC NV_THERM */ + if (ts->soc->use_ccroc) { + throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_LOW); + throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_MED); + throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_HIGH); + } + + /* Thermal HW throttle programming */ + for (i = 0; i < THROTTLE_SIZE; i++) + soctherm_throttle_program(ts, i); + + v = REG_SET_MASK(0, THROT_GLOBAL_ENB_MASK, 1); + if (ts->soc->use_ccroc) { + ccroc_writel(ts, v, CCROC_GLOBAL_CFG); + + v = ccroc_readl(ts, CCROC_SUPER_CCLKG_DIVIDER); + v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1); + ccroc_writel(ts, v, CCROC_SUPER_CCLKG_DIVIDER); + } else { + writel(v, ts->regs + THROT_GLOBAL_CFG); + + v = clk_readl(ts, CAR_SUPER_CCLKG_DIVIDER); + v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1); + clk_writel(ts, v, CAR_SUPER_CCLKG_DIVIDER); + } + + /* initialize stats collection */ + v = STATS_CTL_CLR_DN | STATS_CTL_EN_DN | + STATS_CTL_CLR_UP | STATS_CTL_EN_UP; + writel(v, ts->regs + THERMCTL_STATS_CTL); +} + static void soctherm_init(struct platform_device *pdev) { struct tegra_soctherm *tegra = platform_get_drvdata(pdev); @@ -475,6 +1253,9 @@ static void soctherm_init(struct platform_device *pdev) } writel(pdiv, tegra->regs + SENSOR_PDIV); writel(hotspot, tegra->regs + SENSOR_HOTSPOT_OFF); + + /* Configure hw throttle */ + tegra_soctherm_throttle(&pdev->dev); } static const struct of_device_id tegra_soctherm_of_match[] = { @@ -527,10 +1308,31 @@ static int tegra_soctherm_probe(struct platform_device *pdev) tegra->soc = soc; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "soctherm-reg"); tegra->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(tegra->regs)) + if (IS_ERR(tegra->regs)) { + dev_err(&pdev->dev, "can't get soctherm registers"); return PTR_ERR(tegra->regs); + } + + if (!tegra->soc->use_ccroc) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "car-reg"); + tegra->clk_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tegra->clk_regs)) { + dev_err(&pdev->dev, "can't get car clk registers"); + return PTR_ERR(tegra->clk_regs); + } + } else { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "ccroc-reg"); + tegra->ccroc_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tegra->ccroc_regs)) { + dev_err(&pdev->dev, "can't get ccroc registers"); + return PTR_ERR(tegra->ccroc_regs); + } + } tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm"); if (IS_ERR(tegra->reset)) { @@ -580,6 +1382,8 @@ static int tegra_soctherm_probe(struct platform_device *pdev) if (err) return err; + soctherm_init_hw_throt_cdev(pdev); + soctherm_init(pdev); for (i = 0; i < soc->num_ttgs; ++i) { @@ -593,6 +1397,7 @@ static int tegra_soctherm_probe(struct platform_device *pdev) zone->reg = tegra->regs + soc->ttgs[i]->sensor_temp_offset; zone->dev = &pdev->dev; zone->sg = soc->ttgs[i]; + zone->ts = tegra; z = devm_thermal_zone_of_sensor_register(&pdev->dev, soc->ttgs[i]->id, zone, @@ -608,7 +1413,9 @@ static int tegra_soctherm_probe(struct platform_device *pdev) tegra->thermctl_tzs[soc->ttgs[i]->id] = z; /* Configure hw trip points */ - tegra_soctherm_set_hwtrips(&pdev->dev, soc->ttgs[i], z); + err = tegra_soctherm_set_hwtrips(&pdev->dev, soc->ttgs[i], z); + if (err) + goto disable_clocks; } soctherm_debug_init(pdev); @@ -661,7 +1468,12 @@ static int __maybe_unused soctherm_resume(struct device *dev) struct thermal_zone_device *tz; tz = tegra->thermctl_tzs[soc->ttgs[i]->id]; - tegra_soctherm_set_hwtrips(dev, soc->ttgs[i], tz); + err = tegra_soctherm_set_hwtrips(dev, soc->ttgs[i], tz); + if (err) { + dev_err(&pdev->dev, + "Resume failed: set hwtrips failed\n"); + return err; + } } return 0; diff --git a/drivers/thermal/tegra/soctherm.h b/drivers/thermal/tegra/soctherm.h index 28e18ec4b4c3..e96ca73fd780 100644 --- a/drivers/thermal/tegra/soctherm.h +++ b/drivers/thermal/tegra/soctherm.h @@ -15,6 +15,11 @@ #ifndef __DRIVERS_THERMAL_TEGRA_SOCTHERM_H #define __DRIVERS_THERMAL_TEGRA_SOCTHERM_H +#define THERMCTL_LEVEL0_GROUP_CPU 0x0 +#define THERMCTL_LEVEL0_GROUP_GPU 0x4 +#define THERMCTL_LEVEL0_GROUP_MEM 0x8 +#define THERMCTL_LEVEL0_GROUP_TSENSE 0xc + #define SENSOR_CONFIG2 8 #define SENSOR_CONFIG2_THERMA_MASK (0xffff << 16) #define SENSOR_CONFIG2_THERMA_SHIFT 16 @@ -65,6 +70,9 @@ struct tegra_tsensor_group { u32 thermtrip_enable_mask; u32 thermtrip_any_en_mask; u32 thermtrip_threshold_mask; + u16 thermctl_lvl0_offset; + u32 thermctl_lvl0_up_thresh_mask; + u32 thermctl_lvl0_dn_thresh_mask; }; struct tegra_tsensor_configuration { @@ -103,6 +111,8 @@ struct tegra_soctherm_soc { const unsigned int num_ttgs; const struct tegra_soctherm_fuse *tfuse; const int thresh_grain; + const unsigned int bptt; + const bool use_ccroc; }; int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse, diff --git a/drivers/thermal/tegra/tegra124-soctherm.c b/drivers/thermal/tegra/tegra124-soctherm.c index beb9d36b9c8a..36768630f78c 100644 --- a/drivers/thermal/tegra/tegra124-soctherm.c +++ b/drivers/thermal/tegra/tegra124-soctherm.c @@ -28,7 +28,11 @@ #define TEGRA124_THERMTRIP_CPU_THRESH_MASK (0xff << 8) #define TEGRA124_THERMTRIP_TSENSE_THRESH_MASK 0xff +#define TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK (0xff << 17) +#define TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK (0xff << 9) + #define TEGRA124_THRESH_GRAIN 1000 +#define TEGRA124_BPTT 8 static const struct tegra_tsensor_configuration tegra124_tsensor_config = { .tall = 16300, @@ -51,6 +55,9 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_cpu = { .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA124_THERMTRIP_CPU_EN_MASK, .thermtrip_threshold_mask = TEGRA124_THERMTRIP_CPU_THRESH_MASK, + .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU, + .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK, + .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK, }; static const struct tegra_tsensor_group tegra124_tsensor_group_gpu = { @@ -66,6 +73,9 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_gpu = { .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA124_THERMTRIP_GPU_EN_MASK, .thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK, + .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU, + .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK, + .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK, }; static const struct tegra_tsensor_group tegra124_tsensor_group_pll = { @@ -79,6 +89,9 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_pll = { .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA124_THERMTRIP_TSENSE_EN_MASK, .thermtrip_threshold_mask = TEGRA124_THERMTRIP_TSENSE_THRESH_MASK, + .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE, + .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK, + .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK, }; static const struct tegra_tsensor_group tegra124_tsensor_group_mem = { @@ -94,6 +107,9 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_mem = { .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA124_THERMTRIP_MEM_EN_MASK, .thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK, + .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM, + .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK, + .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK, }; static const struct tegra_tsensor_group *tegra124_tsensor_groups[] = { @@ -193,4 +209,6 @@ const struct tegra_soctherm_soc tegra124_soctherm = { .num_ttgs = ARRAY_SIZE(tegra124_tsensor_groups), .tfuse = &tegra124_soctherm_fuse, .thresh_grain = TEGRA124_THRESH_GRAIN, + .bptt = TEGRA124_BPTT, + .use_ccroc = false, }; diff --git a/drivers/thermal/tegra/tegra132-soctherm.c b/drivers/thermal/tegra/tegra132-soctherm.c index e2aa84e1b307..97fa30501eb1 100644 --- a/drivers/thermal/tegra/tegra132-soctherm.c +++ b/drivers/thermal/tegra/tegra132-soctherm.c @@ -28,7 +28,11 @@ #define TEGRA132_THERMTRIP_CPU_THRESH_MASK (0xff << 8) #define TEGRA132_THERMTRIP_TSENSE_THRESH_MASK 0xff +#define TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK (0xff << 17) +#define TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK (0xff << 9) + #define TEGRA132_THRESH_GRAIN 1000 +#define TEGRA132_BPTT 8 static const struct tegra_tsensor_configuration tegra132_tsensor_config = { .tall = 16300, @@ -51,6 +55,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_cpu = { .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA132_THERMTRIP_CPU_EN_MASK, .thermtrip_threshold_mask = TEGRA132_THERMTRIP_CPU_THRESH_MASK, + .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU, + .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK, + .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK, }; static const struct tegra_tsensor_group tegra132_tsensor_group_gpu = { @@ -66,6 +73,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_gpu = { .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA132_THERMTRIP_GPU_EN_MASK, .thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK, + .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU, + .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK, + .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK, }; static const struct tegra_tsensor_group tegra132_tsensor_group_pll = { @@ -79,6 +89,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_pll = { .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA132_THERMTRIP_TSENSE_EN_MASK, .thermtrip_threshold_mask = TEGRA132_THERMTRIP_TSENSE_THRESH_MASK, + .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE, + .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK, + .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK, }; static const struct tegra_tsensor_group tegra132_tsensor_group_mem = { @@ -94,6 +107,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_mem = { .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA132_THERMTRIP_MEM_EN_MASK, .thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK, + .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM, + .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK, + .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK, }; static const struct tegra_tsensor_group *tegra132_tsensor_groups[] = { @@ -193,4 +209,6 @@ const struct tegra_soctherm_soc tegra132_soctherm = { .num_ttgs = ARRAY_SIZE(tegra132_tsensor_groups), .tfuse = &tegra132_soctherm_fuse, .thresh_grain = TEGRA132_THRESH_GRAIN, + .bptt = TEGRA132_BPTT, + .use_ccroc = true, }; diff --git a/drivers/thermal/tegra/tegra210-soctherm.c b/drivers/thermal/tegra/tegra210-soctherm.c index 19cc0ab66f0e..ad53169a8e95 100644 --- a/drivers/thermal/tegra/tegra210-soctherm.c +++ b/drivers/thermal/tegra/tegra210-soctherm.c @@ -29,7 +29,11 @@ #define TEGRA210_THERMTRIP_CPU_THRESH_MASK (0x1ff << 9) #define TEGRA210_THERMTRIP_TSENSE_THRESH_MASK 0x1ff +#define TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK (0x1ff << 18) +#define TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK (0x1ff << 9) + #define TEGRA210_THRESH_GRAIN 500 +#define TEGRA210_BPTT 9 static const struct tegra_tsensor_configuration tegra210_tsensor_config = { .tall = 16300, @@ -52,6 +56,9 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_cpu = { .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA210_THERMTRIP_CPU_EN_MASK, .thermtrip_threshold_mask = TEGRA210_THERMTRIP_CPU_THRESH_MASK, + .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU, + .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK, + .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK, }; static const struct tegra_tsensor_group tegra210_tsensor_group_gpu = { @@ -67,6 +74,9 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_gpu = { .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA210_THERMTRIP_GPU_EN_MASK, .thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK, + .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU, + .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK, + .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK, }; static const struct tegra_tsensor_group tegra210_tsensor_group_pll = { @@ -80,6 +90,9 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_pll = { .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA210_THERMTRIP_TSENSE_EN_MASK, .thermtrip_threshold_mask = TEGRA210_THERMTRIP_TSENSE_THRESH_MASK, + .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE, + .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK, + .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK, }; static const struct tegra_tsensor_group tegra210_tsensor_group_mem = { @@ -95,6 +108,9 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_mem = { .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK, .thermtrip_enable_mask = TEGRA210_THERMTRIP_MEM_EN_MASK, .thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK, + .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM, + .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK, + .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK, }; static const struct tegra_tsensor_group *tegra210_tsensor_groups[] = { @@ -194,4 +210,6 @@ const struct tegra_soctherm_soc tegra210_soctherm = { .num_ttgs = ARRAY_SIZE(tegra210_tsensor_groups), .tfuse = &tegra210_soctherm_fuse, .thresh_grain = TEGRA210_THRESH_GRAIN, + .bptt = TEGRA210_BPTT, + .use_ccroc = false, }; diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index e2fc6161dded..226b0b4aced6 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -520,6 +520,56 @@ exit: } EXPORT_SYMBOL_GPL(thermal_zone_get_temp); +void thermal_zone_set_trips(struct thermal_zone_device *tz) +{ + int low = -INT_MAX; + int high = INT_MAX; + int trip_temp, hysteresis; + int i, ret; + + mutex_lock(&tz->lock); + + if (!tz->ops->set_trips || !tz->ops->get_trip_hyst) + goto exit; + + for (i = 0; i < tz->trips; i++) { + int trip_low; + + tz->ops->get_trip_temp(tz, i, &trip_temp); + tz->ops->get_trip_hyst(tz, i, &hysteresis); + + trip_low = trip_temp - hysteresis; + + if (trip_low < tz->temperature && trip_low > low) + low = trip_low; + + if (trip_temp > tz->temperature && trip_temp < high) + high = trip_temp; + } + + /* No need to change trip points */ + if (tz->prev_low_trip == low && tz->prev_high_trip == high) + goto exit; + + tz->prev_low_trip = low; + tz->prev_high_trip = high; + + dev_dbg(&tz->device, + "new temperature boundaries: %d < x < %d\n", low, high); + + /* + * Set a temperature window. When this window is left the driver + * must inform the thermal core via thermal_zone_device_update. + */ + ret = tz->ops->set_trips(tz, low, high); + if (ret) + dev_err(&tz->device, "Failed to set trips: %d\n", ret); + +exit: + mutex_unlock(&tz->lock); +} +EXPORT_SYMBOL_GPL(thermal_zone_set_trips); + static void update_temperature(struct thermal_zone_device *tz) { int temp, ret; @@ -557,7 +607,8 @@ static void thermal_zone_device_reset(struct thermal_zone_device *tz) pos->initialized = false; } -void thermal_zone_device_update(struct thermal_zone_device *tz) +void thermal_zone_device_update(struct thermal_zone_device *tz, + enum thermal_notify_event event) { int count; @@ -569,6 +620,10 @@ void thermal_zone_device_update(struct thermal_zone_device *tz) update_temperature(tz); + thermal_zone_set_trips(tz); + + tz->notify_event = event; + for (count = 0; count < tz->trips; count++) handle_thermal_trip(tz, count); } @@ -579,7 +634,7 @@ static void thermal_zone_device_check(struct work_struct *work) struct thermal_zone_device *tz = container_of(work, struct thermal_zone_device, poll_queue.work); - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); } /* sys I/F for thermal zone */ @@ -703,7 +758,7 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr, if (ret) return ret; - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); return count; } @@ -754,6 +809,9 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, */ ret = tz->ops->set_trip_hyst(tz, trip, temperature); + if (!ret) + thermal_zone_set_trips(tz); + return ret ? ret : count; } @@ -822,7 +880,7 @@ passive_store(struct device *dev, struct device_attribute *attr, tz->forced_passive = state; - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); return count; } @@ -913,7 +971,7 @@ emul_temp_store(struct device *dev, struct device_attribute *attr, } if (!ret) - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); return ret ? ret : count; } @@ -1509,7 +1567,8 @@ __thermal_cooling_device_register(struct device_node *np, mutex_lock(&thermal_list_lock); list_for_each_entry(pos, &thermal_tz_list, node) if (atomic_cmpxchg(&pos->need_update, 1, 0)) - thermal_zone_device_update(pos); + thermal_zone_device_update(pos, + THERMAL_EVENT_UNSPECIFIED); mutex_unlock(&thermal_list_lock); return cdev; @@ -1952,7 +2011,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, thermal_zone_device_reset(tz); /* Update the new thermal zone and mark it as already updated. */ if (atomic_cmpxchg(&tz->need_update, 1, 0)) - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); return tz; @@ -2069,6 +2128,36 @@ exit: } EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name); +/** + * thermal_zone_get_slope - return the slope attribute of the thermal zone + * @tz: thermal zone device with the slope attribute + * + * Return: If the thermal zone device has a slope attribute, return it, else + * return 1. + */ +int thermal_zone_get_slope(struct thermal_zone_device *tz) +{ + if (tz && tz->tzp) + return tz->tzp->slope; + return 1; +} +EXPORT_SYMBOL_GPL(thermal_zone_get_slope); + +/** + * thermal_zone_get_offset - return the offset attribute of the thermal zone + * @tz: thermal zone device with the offset attribute + * + * Return: If the thermal zone device has a offset attribute, return it, else + * return 0. + */ +int thermal_zone_get_offset(struct thermal_zone_device *tz) +{ + if (tz && tz->tzp) + return tz->tzp->offset; + return 0; +} +EXPORT_SYMBOL_GPL(thermal_zone_get_offset); + #ifdef CONFIG_NET static const struct genl_multicast_group thermal_event_mcgrps[] = { { .name = THERMAL_GENL_MCAST_GROUP_NAME, }, @@ -2209,7 +2298,8 @@ static int thermal_pm_notify(struct notifier_block *nb, atomic_set(&in_suspend, 0); list_for_each_entry(tz, &thermal_tz_list, node) { thermal_zone_device_reset(tz); - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, + THERMAL_EVENT_UNSPECIFIED); } break; default: diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c index 15c0a9ac2209..0586bd0f2bab 100644 --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c @@ -52,7 +52,7 @@ static void ti_thermal_work(struct work_struct *work) struct ti_thermal_data *data = container_of(work, struct ti_thermal_data, thermal_wq); - thermal_zone_device_update(data->ti_thermal); + thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED); dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n", data->ti_thermal->type); @@ -205,7 +205,7 @@ static int ti_thermal_set_mode(struct thermal_zone_device *thermal, data->mode = mode; ti_bandgap_write_update_interval(bgp, data->sensor_id, data->ti_thermal->polling_delay); - thermal_zone_device_update(data->ti_thermal); + thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED); dev_dbg(&thermal->device, "thermal polling set for duration=%d msec\n", data->ti_thermal->polling_delay); @@ -239,7 +239,7 @@ static int ti_thermal_get_trip_temp(struct thermal_zone_device *thermal, return 0; } -static int __ti_thermal_get_trend(void *p, long *trend) +static int __ti_thermal_get_trend(void *p, int trip, enum thermal_trend *trend) { struct ti_thermal_data *data = p; struct ti_bandgap *bgp; @@ -252,22 +252,6 @@ static int __ti_thermal_get_trend(void *p, long *trend) if (ret) return ret; - *trend = tr; - - return 0; -} - -/* Get the temperature trend callback functions for thermal zone */ -static int ti_thermal_get_trend(struct thermal_zone_device *thermal, - int trip, enum thermal_trend *trend) -{ - int ret; - long tr; - - ret = __ti_thermal_get_trend(thermal->devdata, &tr); - if (ret) - return ret; - if (tr > 0) *trend = THERMAL_TREND_RAISING; else if (tr < 0) @@ -278,6 +262,13 @@ static int ti_thermal_get_trend(struct thermal_zone_device *thermal, return 0; } +/* Get the temperature trend callback functions for thermal zone */ +static int ti_thermal_get_trend(struct thermal_zone_device *thermal, + int trip, enum thermal_trend *trend) +{ + return __ti_thermal_get_trend(thermal->devdata, trip, trend); +} + /* Get critical temperature callback functions for thermal zone */ static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal, int *temp) diff --git a/drivers/thermal/user_space.c b/drivers/thermal/user_space.c index 10adcddc8821..c908150c268d 100644 --- a/drivers/thermal/user_space.c +++ b/drivers/thermal/user_space.c @@ -23,19 +23,30 @@ */ #include <linux/thermal.h> - +#include <linux/slab.h> #include "thermal_core.h" /** * notify_user_space - Notifies user space about thermal events * @tz - thermal_zone_device + * @trip - Trip point index * * This function notifies the user space through UEvents. */ static int notify_user_space(struct thermal_zone_device *tz, int trip) { + char *thermal_prop[5]; + int i; + mutex_lock(&tz->lock); - kobject_uevent(&tz->device.kobj, KOBJ_CHANGE); + thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", tz->type); + thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", tz->temperature); + thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP=%d", trip); + thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", tz->notify_event); + thermal_prop[4] = NULL; + kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, thermal_prop); + for (i = 0; i < 4; ++i) + kfree(thermal_prop[i]); mutex_unlock(&tz->lock); return 0; } diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 97f0a2bd93ed..95f4c1bcdb4c 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -348,7 +348,8 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) } if (notify) { pr_debug("thermal_zone_device_update\n"); - thermal_zone_device_update(phdev->tzone); + thermal_zone_device_update(phdev->tzone, + THERMAL_EVENT_UNSPECIFIED); } } diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 88b008fb8a4e..af2f117208f1 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -284,12 +284,14 @@ config FB_PM2_FIFO_DISCONNECT config FB_ARMCLCD tristate "ARM PrimeCell PL110 support" depends on ARM || ARM64 || COMPILE_TEST - depends on FB && ARM_AMBA + depends on FB && ARM_AMBA && HAS_IOMEM select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select FB_MODE_HELPERS if OF select VIDEOMODE_HELPERS if OF + select BACKLIGHT_LCD_SUPPORT if OF + select BACKLIGHT_CLASS_DEVICE if OF help This framebuffer device driver is for the ARM PrimeCell PL110 Colour LCD controller. ARM PrimeCells provide the building @@ -305,6 +307,8 @@ config PLAT_VERSATILE_CLCD def_bool ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS || ARCH_INTEGRATOR depends on ARM depends on FB_ARMCLCD && FB=y + select REGMAP + select MFD_SYSCON config FB_ACORN bool "Acorn VIDC support" @@ -2443,7 +2447,6 @@ config FB_SIMPLE source "drivers/video/fbdev/omap/Kconfig" source "drivers/video/fbdev/omap2/Kconfig" -source "drivers/video/fbdev/exynos/Kconfig" source "drivers/video/fbdev/mmp/Kconfig" config FB_SH_MOBILE_MERAM diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index f6731867dd26..ee8c81405a7f 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -6,8 +6,6 @@ obj-y += core/ -obj-$(CONFIG_EXYNOS_VIDEO) += exynos/ - obj-$(CONFIG_FB_MACMODES) += macmodes.o obj-$(CONFIG_FB_WMT_GE_ROPS) += wmt_ge_rops.o @@ -79,6 +77,7 @@ obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o +obj-$(CONFIG_ARCH_NOMADIK) += amba-clcd-nomadik.o obj-$(CONFIG_PLAT_VERSATILE_CLCD) += amba-clcd-versatile.o obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o obj-$(CONFIG_FB_68328) += 68328fb.o diff --git a/drivers/video/fbdev/amba-clcd-nomadik.c b/drivers/video/fbdev/amba-clcd-nomadik.c new file mode 100644 index 000000000000..0c06fcaaa6e8 --- /dev/null +++ b/drivers/video/fbdev/amba-clcd-nomadik.c @@ -0,0 +1,259 @@ +#include <linux/amba/bus.h> +#include <linux/amba/clcd.h> +#include <linux/gpio/consumer.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/delay.h> +#include <linux/bitops.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> + +#include "amba-clcd-nomadik.h" + +static struct gpio_desc *grestb; +static struct gpio_desc *scen; +static struct gpio_desc *scl; +static struct gpio_desc *sda; + +static u8 tpg110_readwrite_reg(bool write, u8 address, u8 outval) +{ + int i; + u8 inval = 0; + + /* Assert SCEN */ + gpiod_set_value_cansleep(scen, 1); + ndelay(150); + /* Hammer out the address */ + for (i = 5; i >= 0; i--) { + if (address & BIT(i)) + gpiod_set_value_cansleep(sda, 1); + else + gpiod_set_value_cansleep(sda, 0); + ndelay(150); + /* Send an SCL pulse */ + gpiod_set_value_cansleep(scl, 1); + ndelay(160); + gpiod_set_value_cansleep(scl, 0); + ndelay(160); + } + + if (write) { + /* WRITE */ + gpiod_set_value_cansleep(sda, 0); + } else { + /* READ */ + gpiod_set_value_cansleep(sda, 1); + } + ndelay(150); + /* Send an SCL pulse */ + gpiod_set_value_cansleep(scl, 1); + ndelay(160); + gpiod_set_value_cansleep(scl, 0); + ndelay(160); + + if (!write) + /* HiZ turn-around cycle */ + gpiod_direction_input(sda); + ndelay(150); + /* Send an SCL pulse */ + gpiod_set_value_cansleep(scl, 1); + ndelay(160); + gpiod_set_value_cansleep(scl, 0); + ndelay(160); + + /* Hammer in/out the data */ + for (i = 7; i >= 0; i--) { + int value; + + if (write) { + value = !!(outval & BIT(i)); + gpiod_set_value_cansleep(sda, value); + } else { + value = gpiod_get_value(sda); + if (value) + inval |= BIT(i); + } + ndelay(150); + /* Send an SCL pulse */ + gpiod_set_value_cansleep(scl, 1); + ndelay(160); + gpiod_set_value_cansleep(scl, 0); + ndelay(160); + } + + gpiod_direction_output(sda, 0); + /* Deassert SCEN */ + gpiod_set_value_cansleep(scen, 0); + /* Satisfies SCEN pulse width */ + udelay(1); + + return inval; +} + +static u8 tpg110_read_reg(u8 address) +{ + return tpg110_readwrite_reg(false, address, 0); +} + +static void tpg110_write_reg(u8 address, u8 outval) +{ + tpg110_readwrite_reg(true, address, outval); +} + +static void tpg110_startup(struct device *dev) +{ + u8 val; + + dev_info(dev, "TPG110 display enable\n"); + /* De-assert the reset signal */ + gpiod_set_value_cansleep(grestb, 0); + mdelay(1); + dev_info(dev, "de-asserted GRESTB\n"); + + /* Test display communication */ + tpg110_write_reg(0x00, 0x55); + val = tpg110_read_reg(0x00); + if (val == 0x55) + dev_info(dev, "passed communication test\n"); + val = tpg110_read_reg(0x01); + dev_info(dev, "TPG110 chip ID: %d version: %d\n", + val>>4, val&0x0f); + + /* Show display resolution */ + val = tpg110_read_reg(0x02); + val &= 7; + switch (val) { + case 0x0: + dev_info(dev, "IN 400x240 RGB -> OUT 800x480 RGB (dual scan)"); + break; + case 0x1: + dev_info(dev, "IN 480x272 RGB -> OUT 800x480 RGB (dual scan)"); + break; + case 0x4: + dev_info(dev, "480x640 RGB"); + break; + case 0x5: + dev_info(dev, "480x272 RGB"); + break; + case 0x6: + dev_info(dev, "640x480 RGB"); + break; + case 0x7: + dev_info(dev, "800x480 RGB"); + break; + default: + dev_info(dev, "ILLEGAL RESOLUTION"); + break; + } + + val = tpg110_read_reg(0x03); + dev_info(dev, "resolution is controlled by %s\n", + (val & BIT(7)) ? "software" : "hardware"); +} + +static void tpg110_enable(struct clcd_fb *fb) +{ + struct device *dev = &fb->dev->dev; + static bool startup; + u8 val; + + if (!startup) { + tpg110_startup(dev); + startup = true; + } + + /* Take chip out of standby */ + val = tpg110_read_reg(0x03); + val |= BIT(0); + tpg110_write_reg(0x03, val); +} + +static void tpg110_disable(struct clcd_fb *fb) +{ + u8 val; + + dev_info(&fb->dev->dev, "TPG110 display disable\n"); + val = tpg110_read_reg(0x03); + /* Put into standby */ + val &= ~BIT(0); + tpg110_write_reg(0x03, val); +} + +static void tpg110_init(struct device *dev, struct device_node *np, + struct clcd_board *board) +{ + dev_info(dev, "TPG110 display init\n"); + + grestb = devm_get_gpiod_from_child(dev, "grestb", &np->fwnode); + if (IS_ERR(grestb)) { + dev_err(dev, "no GRESTB GPIO\n"); + return; + } + /* This asserts the GRESTB signal, putting the display into reset */ + gpiod_direction_output(grestb, 1); + + scen = devm_get_gpiod_from_child(dev, "scen", &np->fwnode); + if (IS_ERR(scen)) { + dev_err(dev, "no SCEN GPIO\n"); + return; + } + gpiod_direction_output(scen, 0); + scl = devm_get_gpiod_from_child(dev, "scl", &np->fwnode); + if (IS_ERR(scl)) { + dev_err(dev, "no SCL GPIO\n"); + return; + } + gpiod_direction_output(scl, 0); + sda = devm_get_gpiod_from_child(dev, "sda", &np->fwnode); + if (IS_ERR(sda)) { + dev_err(dev, "no SDA GPIO\n"); + return; + } + gpiod_direction_output(sda, 0); + board->enable = tpg110_enable; + board->disable = tpg110_disable; +} + +int nomadik_clcd_init_panel(struct clcd_fb *fb, + struct device_node *endpoint) +{ + struct device_node *panel; + + panel = of_graph_get_remote_port_parent(endpoint); + if (!panel) + return -ENODEV; + + if (of_device_is_compatible(panel, "tpo,tpg110")) + tpg110_init(&fb->dev->dev, panel, fb->board); + else + dev_info(&fb->dev->dev, "unknown panel\n"); + + /* Unknown panel, fall through */ + return 0; +} +EXPORT_SYMBOL_GPL(nomadik_clcd_init_panel); + +#define PMU_CTRL_OFFSET 0x0000 +#define PMU_CTRL_LCDNDIF BIT(26) + +int nomadik_clcd_init_board(struct amba_device *adev, + struct clcd_board *board) +{ + struct regmap *pmu_regmap; + + dev_info(&adev->dev, "Nomadik CLCD board init\n"); + pmu_regmap = + syscon_regmap_lookup_by_compatible("stericsson,nomadik-pmu"); + if (IS_ERR(pmu_regmap)) { + dev_err(&adev->dev, "could not find PMU syscon regmap\n"); + return PTR_ERR(pmu_regmap); + } + regmap_update_bits(pmu_regmap, + PMU_CTRL_OFFSET, + PMU_CTRL_LCDNDIF, + 0); + dev_info(&adev->dev, "set PMU mux to CLCD mode\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(nomadik_clcd_init_board); diff --git a/drivers/video/fbdev/amba-clcd-nomadik.h b/drivers/video/fbdev/amba-clcd-nomadik.h new file mode 100644 index 000000000000..50aa9bda69fd --- /dev/null +++ b/drivers/video/fbdev/amba-clcd-nomadik.h @@ -0,0 +1,24 @@ +#ifndef _AMBA_CLCD_NOMADIK_H +#define _AMBA_CLCD_NOMADIK_H + +#include <linux/amba/bus.h> + +#ifdef CONFIG_ARCH_NOMADIK +int nomadik_clcd_init_board(struct amba_device *adev, + struct clcd_board *board); +int nomadik_clcd_init_panel(struct clcd_fb *fb, + struct device_node *endpoint); +#else +static inline int nomadik_clcd_init_board(struct amba_device *adev, + struct clcd_board *board) +{ + return 0; +} +static inline int nomadik_clcd_init_panel(struct clcd_fb *fb, + struct device_node *endpoint) +{ + return 0; +} +#endif + +#endif /* inclusion guard */ diff --git a/drivers/video/fbdev/amba-clcd-versatile.c b/drivers/video/fbdev/amba-clcd-versatile.c index a8a22daa3f9d..19ad8645d93c 100644 --- a/drivers/video/fbdev/amba-clcd-versatile.c +++ b/drivers/video/fbdev/amba-clcd-versatile.c @@ -3,6 +3,12 @@ #include <linux/amba/bus.h> #include <linux/amba/clcd.h> #include <linux/platform_data/video-clcd-versatile.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include <linux/bitops.h> +#include "amba-clcd-versatile.h" static struct clcd_panel vga = { .mode = { @@ -178,3 +184,392 @@ void versatile_clcd_remove_dma(struct clcd_fb *fb) dma_free_wc(&fb->dev->dev, fb->fb.fix.smem_len, fb->fb.screen_base, fb->fb.fix.smem_start); } + +#ifdef CONFIG_OF + +static struct regmap *versatile_syscon_map; +static struct regmap *versatile_ib2_map; + +/* + * We detect the different syscon types from the compatible strings. + */ +enum versatile_clcd { + INTEGRATOR_CLCD_CM, + VERSATILE_CLCD, + REALVIEW_CLCD_EB, + REALVIEW_CLCD_PB1176, + REALVIEW_CLCD_PB11MP, + REALVIEW_CLCD_PBA8, + REALVIEW_CLCD_PBX, +}; + +static const struct of_device_id versatile_clcd_of_match[] = { + { + .compatible = "arm,core-module-integrator", + .data = (void *)INTEGRATOR_CLCD_CM, + }, + { + .compatible = "arm,versatile-sysreg", + .data = (void *)VERSATILE_CLCD, + }, + { + .compatible = "arm,realview-eb-syscon", + .data = (void *)REALVIEW_CLCD_EB, + }, + { + .compatible = "arm,realview-pb1176-syscon", + .data = (void *)REALVIEW_CLCD_PB1176, + }, + { + .compatible = "arm,realview-pb11mp-syscon", + .data = (void *)REALVIEW_CLCD_PB11MP, + }, + { + .compatible = "arm,realview-pba8-syscon", + .data = (void *)REALVIEW_CLCD_PBA8, + }, + { + .compatible = "arm,realview-pbx-syscon", + .data = (void *)REALVIEW_CLCD_PBX, + }, + {}, +}; + +/* + * Core module CLCD control on the Integrator/CP, bits + * 8 thru 19 of the CM_CONTROL register controls a bunch + * of CLCD settings. + */ +#define INTEGRATOR_HDR_CTRL_OFFSET 0x0C +#define INTEGRATOR_CLCD_LCDBIASEN BIT(8) +#define INTEGRATOR_CLCD_LCDBIASUP BIT(9) +#define INTEGRATOR_CLCD_LCDBIASDN BIT(10) +/* Bits 11,12,13 controls the LCD type */ +#define INTEGRATOR_CLCD_LCDMUX_MASK (BIT(11)|BIT(12)|BIT(13)) +#define INTEGRATOR_CLCD_LCDMUX_LCD24 BIT(11) +#define INTEGRATOR_CLCD_LCDMUX_VGA565 BIT(12) +#define INTEGRATOR_CLCD_LCDMUX_SHARP (BIT(11)|BIT(12)) +#define INTEGRATOR_CLCD_LCDMUX_VGA555 BIT(13) +#define INTEGRATOR_CLCD_LCDMUX_VGA24 (BIT(11)|BIT(12)|BIT(13)) +#define INTEGRATOR_CLCD_LCD0_EN BIT(14) +#define INTEGRATOR_CLCD_LCD1_EN BIT(15) +/* R/L flip on Sharp */ +#define INTEGRATOR_CLCD_LCD_STATIC1 BIT(16) +/* U/D flip on Sharp */ +#define INTEGRATOR_CLCD_LCD_STATIC2 BIT(17) +/* No connection on Sharp */ +#define INTEGRATOR_CLCD_LCD_STATIC BIT(18) +/* 0 = 24bit VGA, 1 = 18bit VGA */ +#define INTEGRATOR_CLCD_LCD_N24BITEN BIT(19) + +#define INTEGRATOR_CLCD_MASK (INTEGRATOR_CLCD_LCDBIASEN | \ + INTEGRATOR_CLCD_LCDBIASUP | \ + INTEGRATOR_CLCD_LCDBIASDN | \ + INTEGRATOR_CLCD_LCDMUX_MASK | \ + INTEGRATOR_CLCD_LCD0_EN | \ + INTEGRATOR_CLCD_LCD1_EN | \ + INTEGRATOR_CLCD_LCD_STATIC1 | \ + INTEGRATOR_CLCD_LCD_STATIC2 | \ + INTEGRATOR_CLCD_LCD_STATIC | \ + INTEGRATOR_CLCD_LCD_N24BITEN) + +static void integrator_clcd_enable(struct clcd_fb *fb) +{ + struct fb_var_screeninfo *var = &fb->fb.var; + u32 val; + + dev_info(&fb->dev->dev, "enable Integrator CLCD connectors\n"); + + /* FIXME: really needed? */ + val = INTEGRATOR_CLCD_LCD_STATIC1 | INTEGRATOR_CLCD_LCD_STATIC2 | + INTEGRATOR_CLCD_LCD0_EN | INTEGRATOR_CLCD_LCD1_EN; + if (var->bits_per_pixel <= 8 || + (var->bits_per_pixel == 16 && var->green.length == 5)) + /* Pseudocolor, RGB555, BGR555 */ + val |= INTEGRATOR_CLCD_LCDMUX_VGA555; + else if (fb->fb.var.bits_per_pixel <= 16) + /* truecolor RGB565 */ + val |= INTEGRATOR_CLCD_LCDMUX_VGA565; + else + val = 0; /* no idea for this, don't trust the docs */ + + regmap_update_bits(versatile_syscon_map, + INTEGRATOR_HDR_CTRL_OFFSET, + INTEGRATOR_CLCD_MASK, + val); +} + +/* + * This configuration register in the Versatile and RealView + * family is uniformly present but appears more and more + * unutilized starting with the RealView series. + */ +#define SYS_CLCD 0x50 +#define SYS_CLCD_MODE_MASK (BIT(0)|BIT(1)) +#define SYS_CLCD_MODE_888 0 +#define SYS_CLCD_MODE_5551 BIT(0) +#define SYS_CLCD_MODE_565_R_LSB BIT(1) +#define SYS_CLCD_MODE_565_B_LSB (BIT(0)|BIT(1)) +#define SYS_CLCD_CONNECTOR_MASK (BIT(2)|BIT(3)|BIT(4)|BIT(5)) +#define SYS_CLCD_NLCDIOON BIT(2) +#define SYS_CLCD_VDDPOSSWITCH BIT(3) +#define SYS_CLCD_PWR3V5SWITCH BIT(4) +#define SYS_CLCD_VDDNEGSWITCH BIT(5) +#define SYS_CLCD_TSNSS BIT(6) /* touchscreen enable */ +#define SYS_CLCD_SSPEXP BIT(7) /* SSP expansion enable */ + +/* The Versatile can detect the connected panel type */ +#define SYS_CLCD_CLCDID_MASK (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)) +#define SYS_CLCD_ID_SANYO_3_8 (0x00 << 8) +#define SYS_CLCD_ID_SHARP_8_4 (0x01 << 8) +#define SYS_CLCD_ID_EPSON_2_2 (0x02 << 8) +#define SYS_CLCD_ID_SANYO_2_5 (0x07 << 8) +#define SYS_CLCD_ID_VGA (0x1f << 8) + +#define SYS_CLCD_TSNDAV BIT(13) /* data ready from TS */ + +/* IB2 control register for the Versatile daughterboard */ +#define IB2_CTRL 0x00 +#define IB2_CTRL_LCD_SD BIT(1) /* 1 = shut down LCD */ +#define IB2_CTRL_LCD_BL_ON BIT(0) +#define IB2_CTRL_LCD_MASK (BIT(0)|BIT(1)) + +static void versatile_clcd_disable(struct clcd_fb *fb) +{ + dev_info(&fb->dev->dev, "disable Versatile CLCD connectors\n"); + regmap_update_bits(versatile_syscon_map, + SYS_CLCD, + SYS_CLCD_CONNECTOR_MASK, + 0); + + /* If we're on an IB2 daughterboard, turn off display */ + if (versatile_ib2_map) { + dev_info(&fb->dev->dev, "disable IB2 display\n"); + regmap_update_bits(versatile_ib2_map, + IB2_CTRL, + IB2_CTRL_LCD_MASK, + IB2_CTRL_LCD_SD); + } +} + +static void versatile_clcd_enable(struct clcd_fb *fb) +{ + struct fb_var_screeninfo *var = &fb->fb.var; + u32 val = 0; + + dev_info(&fb->dev->dev, "enable Versatile CLCD connectors\n"); + switch (var->green.length) { + case 5: + val |= SYS_CLCD_MODE_5551; + break; + case 6: + if (var->red.offset == 0) + val |= SYS_CLCD_MODE_565_R_LSB; + else + val |= SYS_CLCD_MODE_565_B_LSB; + break; + case 8: + val |= SYS_CLCD_MODE_888; + break; + } + + /* Set up the MUX */ + regmap_update_bits(versatile_syscon_map, + SYS_CLCD, + SYS_CLCD_MODE_MASK, + val); + + /* Then enable the display */ + regmap_update_bits(versatile_syscon_map, + SYS_CLCD, + SYS_CLCD_CONNECTOR_MASK, + SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH); + + /* If we're on an IB2 daughterboard, turn on display */ + if (versatile_ib2_map) { + dev_info(&fb->dev->dev, "enable IB2 display\n"); + regmap_update_bits(versatile_ib2_map, + IB2_CTRL, + IB2_CTRL_LCD_MASK, + IB2_CTRL_LCD_BL_ON); + } +} + +static void versatile_clcd_decode(struct clcd_fb *fb, struct clcd_regs *regs) +{ + clcdfb_decode(fb, regs); + + /* Always clear BGR for RGB565: we do the routing externally */ + if (fb->fb.var.green.length == 6) + regs->cntl &= ~CNTL_BGR; +} + +static void realview_clcd_disable(struct clcd_fb *fb) +{ + dev_info(&fb->dev->dev, "disable RealView CLCD connectors\n"); + regmap_update_bits(versatile_syscon_map, + SYS_CLCD, + SYS_CLCD_CONNECTOR_MASK, + 0); +} + +static void realview_clcd_enable(struct clcd_fb *fb) +{ + dev_info(&fb->dev->dev, "enable RealView CLCD connectors\n"); + regmap_update_bits(versatile_syscon_map, + SYS_CLCD, + SYS_CLCD_CONNECTOR_MASK, + SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH); +} + +struct versatile_panel { + u32 id; + char *compatible; + bool ib2; +}; + +static const struct versatile_panel versatile_panels[] = { + { + .id = SYS_CLCD_ID_VGA, + .compatible = "VGA", + }, + { + .id = SYS_CLCD_ID_SANYO_3_8, + .compatible = "sanyo,tm38qv67a02a", + }, + { + .id = SYS_CLCD_ID_SHARP_8_4, + .compatible = "sharp,lq084v1dg21", + }, + { + .id = SYS_CLCD_ID_EPSON_2_2, + .compatible = "epson,l2f50113t00", + }, + { + .id = SYS_CLCD_ID_SANYO_2_5, + .compatible = "sanyo,alr252rgt", + .ib2 = true, + }, +}; + +static void versatile_panel_probe(struct device *dev, + struct device_node *endpoint) +{ + struct versatile_panel const *vpanel = NULL; + struct device_node *panel = NULL; + u32 val; + int ret; + int i; + + /* + * The Versatile CLCD has a panel auto-detection mechanism. + * We use this and look for the compatible panel in the + * device tree. + */ + ret = regmap_read(versatile_syscon_map, SYS_CLCD, &val); + if (ret) { + dev_err(dev, "cannot read CLCD syscon register\n"); + return; + } + val &= SYS_CLCD_CLCDID_MASK; + + /* First find corresponding panel information */ + for (i = 0; i < ARRAY_SIZE(versatile_panels); i++) { + vpanel = &versatile_panels[i]; + + if (val == vpanel->id) { + dev_err(dev, "autodetected panel \"%s\"\n", + vpanel->compatible); + break; + } + } + if (i == ARRAY_SIZE(versatile_panels)) { + dev_err(dev, "could not auto-detect panel\n"); + return; + } + + panel = of_graph_get_remote_port_parent(endpoint); + if (!panel) { + dev_err(dev, "could not locate panel in DT\n"); + return; + } + if (!of_device_is_compatible(panel, vpanel->compatible)) + dev_err(dev, "panel in DT is not compatible with the " + "auto-detected panel, continuing anyway\n"); + + /* + * If we have a Sanyo 2.5" port + * that we're running on an IB2 and proceed to look for the + * IB2 syscon regmap. + */ + if (!vpanel->ib2) + return; + + versatile_ib2_map = syscon_regmap_lookup_by_compatible( + "arm,versatile-ib2-syscon"); + if (IS_ERR(versatile_ib2_map)) { + dev_err(dev, "could not locate IB2 control register\n"); + versatile_ib2_map = NULL; + return; + } +} + +int versatile_clcd_init_panel(struct clcd_fb *fb, + struct device_node *endpoint) +{ + const struct of_device_id *clcd_id; + enum versatile_clcd versatile_clcd_type; + struct device_node *np; + struct regmap *map; + struct device *dev = &fb->dev->dev; + + np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match, + &clcd_id); + if (!np) { + dev_err(dev, "no Versatile syscon node\n"); + return -ENODEV; + } + versatile_clcd_type = (enum versatile_clcd)clcd_id->data; + + map = syscon_node_to_regmap(np); + if (IS_ERR(map)) { + dev_err(dev, "no Versatile syscon regmap\n"); + return PTR_ERR(map); + } + + switch (versatile_clcd_type) { + case INTEGRATOR_CLCD_CM: + versatile_syscon_map = map; + fb->board->enable = integrator_clcd_enable; + /* Override the caps, we have only these */ + fb->board->caps = CLCD_CAP_5551 | CLCD_CAP_RGB565 | + CLCD_CAP_888; + dev_info(dev, "set up callbacks for Integrator PL110\n"); + break; + case VERSATILE_CLCD: + versatile_syscon_map = map; + fb->board->enable = versatile_clcd_enable; + fb->board->disable = versatile_clcd_disable; + fb->board->decode = versatile_clcd_decode; + versatile_panel_probe(dev, endpoint); + dev_info(dev, "set up callbacks for Versatile\n"); + break; + case REALVIEW_CLCD_EB: + case REALVIEW_CLCD_PB1176: + case REALVIEW_CLCD_PB11MP: + case REALVIEW_CLCD_PBA8: + case REALVIEW_CLCD_PBX: + versatile_syscon_map = map; + fb->board->enable = realview_clcd_enable; + fb->board->disable = realview_clcd_disable; + dev_info(dev, "set up callbacks for RealView PL111\n"); + break; + default: + dev_info(dev, "unknown Versatile system controller\n"); + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(versatile_clcd_init_panel); +#endif diff --git a/drivers/video/fbdev/amba-clcd-versatile.h b/drivers/video/fbdev/amba-clcd-versatile.h new file mode 100644 index 000000000000..1b14359c2cf6 --- /dev/null +++ b/drivers/video/fbdev/amba-clcd-versatile.h @@ -0,0 +1,17 @@ +/* + * Special local versatile callbacks + */ +#include <linux/of.h> +#include <linux/amba/bus.h> +#include <linux/platform_data/video-clcd-versatile.h> + +#if defined(CONFIG_PLAT_VERSATILE_CLCD) && defined(CONFIG_OF) +int versatile_clcd_init_panel(struct clcd_fb *fb, + struct device_node *endpoint); +#else +static inline int versatile_clcd_init_panel(struct clcd_fb *fb, + struct device_node *endpoint) +{ + return 0; +} +#endif diff --git a/drivers/video/fbdev/amba-clcd.c b/drivers/video/fbdev/amba-clcd.c index 9b158869cb89..ec2671d98abc 100644 --- a/drivers/video/fbdev/amba-clcd.c +++ b/drivers/video/fbdev/amba-clcd.c @@ -30,10 +30,14 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_graph.h> +#include <linux/backlight.h> #include <video/display_timing.h> #include <video/of_display_timing.h> #include <video/videomode.h> +#include "amba-clcd-nomadik.h" +#include "amba-clcd-versatile.h" + #define to_clcd(info) container_of(info, struct clcd_fb, fb) /* This is limited to 16 characters when displayed by X startup */ @@ -71,6 +75,11 @@ static void clcdfb_disable(struct clcd_fb *fb) if (fb->board->disable) fb->board->disable(fb); + if (fb->panel->backlight) { + fb->panel->backlight->props.power = FB_BLANK_POWERDOWN; + backlight_update_status(fb->panel->backlight); + } + val = readl(fb->regs + fb->off_cntl); if (val & CNTL_LCDPWR) { val &= ~CNTL_LCDPWR; @@ -117,6 +126,14 @@ static void clcdfb_enable(struct clcd_fb *fb, u32 cntl) writel(cntl, fb->regs + fb->off_cntl); /* + * Turn on backlight + */ + if (fb->panel->backlight) { + fb->panel->backlight->props.power = FB_BLANK_UNBLANK; + backlight_update_status(fb->panel->backlight); + } + + /* * finally, enable the interface. */ if (fb->board->enable) @@ -211,6 +228,15 @@ clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var) var->blue.length = 4; } break; + case 24: + if (fb->vendor->packed_24_bit_pixels) { + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + } else { + ret = -EINVAL; + } + break; case 32: /* If we can't do 888, reject */ caps &= CLCD_CAP_888; @@ -297,6 +323,12 @@ static int clcdfb_set_par(struct fb_info *info) clcdfb_disable(fb); + /* Some variants must be clocked here */ + if (fb->vendor->clock_timregs && !fb->clk_enabled) { + fb->clk_enabled = true; + clk_enable(fb->clk); + } + writel(regs.tim0, fb->regs + CLCD_TIM0); writel(regs.tim1, fb->regs + CLCD_TIM1); writel(regs.tim2, fb->regs + CLCD_TIM2); @@ -551,7 +583,7 @@ static int clcdfb_register(struct clcd_fb *fb) #ifdef CONFIG_OF static int clcdfb_of_get_dpi_panel_mode(struct device_node *node, - struct fb_videomode *mode) + struct clcd_panel *clcd_panel) { int err; struct display_timing timing; @@ -563,10 +595,31 @@ static int clcdfb_of_get_dpi_panel_mode(struct device_node *node, videomode_from_timing(&timing, &video); - err = fb_videomode_from_videomode(&video, mode); + err = fb_videomode_from_videomode(&video, &clcd_panel->mode); if (err) return err; + /* Set up some inversion flags */ + if (timing.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) + clcd_panel->tim2 |= TIM2_IPC; + else if (!(timing.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)) + /* + * To preserve backwards compatibility, the IPC (inverted + * pixel clock) flag needs to be set on any display that + * doesn't explicitly specify that the pixel clock is + * active on the negative or positive edge. + */ + clcd_panel->tim2 |= TIM2_IPC; + + if (timing.flags & DISPLAY_FLAGS_HSYNC_LOW) + clcd_panel->tim2 |= TIM2_IHS; + + if (timing.flags & DISPLAY_FLAGS_VSYNC_LOW) + clcd_panel->tim2 |= TIM2_IVS; + + if (timing.flags & DISPLAY_FLAGS_DE_LOW) + clcd_panel->tim2 |= TIM2_IOE; + return 0; } @@ -576,11 +629,34 @@ static int clcdfb_snprintf_mode(char *buf, int size, struct fb_videomode *mode) mode->refresh); } +static int clcdfb_of_get_backlight(struct device_node *endpoint, + struct clcd_panel *clcd_panel) +{ + struct device_node *panel; + struct device_node *backlight; + + panel = of_graph_get_remote_port_parent(endpoint); + if (!panel) + return -ENODEV; + + /* Look up the optional backlight phandle */ + backlight = of_parse_phandle(panel, "backlight", 0); + if (backlight) { + clcd_panel->backlight = of_find_backlight_by_node(backlight); + of_node_put(backlight); + + if (!clcd_panel->backlight) + return -EPROBE_DEFER; + } + return 0; +} + static int clcdfb_of_get_mode(struct device *dev, struct device_node *endpoint, - struct fb_videomode *mode) + struct clcd_panel *clcd_panel) { int err; struct device_node *panel; + struct fb_videomode *mode; char *name; int len; @@ -590,11 +666,12 @@ static int clcdfb_of_get_mode(struct device *dev, struct device_node *endpoint, /* Only directly connected DPI panels supported for now */ if (of_device_is_compatible(panel, "panel-dpi")) - err = clcdfb_of_get_dpi_panel_mode(panel, mode); + err = clcdfb_of_get_dpi_panel_mode(panel, clcd_panel); else err = -ENOENT; if (err) return err; + mode = &clcd_panel->mode; len = clcdfb_snprintf_mode(NULL, 0, mode); name = devm_kzalloc(dev, len + 1, GFP_KERNEL); @@ -616,6 +693,7 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0) } panels[] = { { 0x110, 1, 7, 13, CLCD_CAP_5551 }, { 0x110, 0, 8, 16, CLCD_CAP_888 }, + { 0x110, 16, 8, 0, CLCD_CAP_888 }, { 0x111, 4, 14, 20, CLCD_CAP_444 }, { 0x111, 3, 11, 19, CLCD_CAP_444 | CLCD_CAP_5551 }, { 0x111, 3, 10, 19, CLCD_CAP_444 | CLCD_CAP_5551 | @@ -625,8 +703,8 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0) }; int i; - /* Bypass pixel clock divider, data output on the falling edge */ - fb->panel->tim2 = TIM2_BCD | TIM2_IPC; + /* Bypass pixel clock divider */ + fb->panel->tim2 |= TIM2_BCD; /* TFT display, vert. comp. interrupt at the start of the back porch */ fb->panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1); @@ -643,6 +721,49 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0) fb->panel->caps = panels[i].caps; } + /* + * If we actually physically connected the R lines to B and + * vice versa + */ + if (r0 != 0 && b0 == 0) + fb->panel->bgr_connection = true; + + if (fb->panel->caps && fb->vendor->st_bitmux_control) { + /* + * Set up the special bits for the Nomadik control register + * (other platforms tend to do this through an external + * register). + */ + + /* Offset of the highest used color */ + int maxoff = max3(r0, g0, b0); + /* Most significant bit out, highest used bit */ + int msb = 0; + + if (fb->panel->caps & CLCD_CAP_888) { + msb = maxoff + 8 - 1; + } else if (fb->panel->caps & CLCD_CAP_565) { + msb = maxoff + 5 - 1; + fb->panel->cntl |= CNTL_ST_1XBPP_565; + } else if (fb->panel->caps & CLCD_CAP_5551) { + msb = maxoff + 5 - 1; + fb->panel->cntl |= CNTL_ST_1XBPP_5551; + } else if (fb->panel->caps & CLCD_CAP_444) { + msb = maxoff + 4 - 1; + fb->panel->cntl |= CNTL_ST_1XBPP_444; + } + + /* Send out as many bits as we need */ + if (msb > 17) + fb->panel->cntl |= CNTL_ST_CDWID_24; + else if (msb > 15) + fb->panel->cntl |= CNTL_ST_CDWID_18; + else if (msb > 11) + fb->panel->cntl |= CNTL_ST_CDWID_16; + else + fb->panel->cntl |= CNTL_ST_CDWID_12; + } + return fb->panel->caps ? 0 : -EINVAL; } @@ -658,11 +779,24 @@ static int clcdfb_of_init_display(struct clcd_fb *fb) if (!fb->panel) return -ENOMEM; + /* + * Fetch the panel endpoint. + */ endpoint = of_graph_get_next_endpoint(fb->dev->dev.of_node, NULL); if (!endpoint) return -ENODEV; - err = clcdfb_of_get_mode(&fb->dev->dev, endpoint, &fb->panel->mode); + if (fb->vendor->init_panel) { + err = fb->vendor->init_panel(fb, endpoint); + if (err) + return err; + } + + err = clcdfb_of_get_backlight(endpoint, fb->panel); + if (err) + return err; + + err = clcdfb_of_get_mode(&fb->dev->dev, endpoint, fb->panel); if (err) return err; @@ -693,11 +827,11 @@ static int clcdfb_of_init_display(struct clcd_fb *fb) if (of_property_read_u32_array(endpoint, "arm,pl11x,tft-r0g0b0-pads", - tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) == 0) - return clcdfb_of_init_tft_panel(fb, tft_r0b0g0[0], - tft_r0b0g0[1], tft_r0b0g0[2]); + tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) != 0) + return -ENOENT; - return -ENOENT; + return clcdfb_of_init_tft_panel(fb, tft_r0b0g0[0], + tft_r0b0g0[1], tft_r0b0g0[2]); } static int clcdfb_of_vram_setup(struct clcd_fb *fb) @@ -818,6 +952,7 @@ static struct clcd_board *clcdfb_of_get_board(struct amba_device *dev) static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id) { struct clcd_board *board = dev_get_platdata(&dev->dev); + struct clcd_vendor_data *vendor = id->data; struct clcd_fb *fb; int ret; @@ -827,6 +962,12 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id) if (!board) return -EINVAL; + if (vendor->init_board) { + ret = vendor->init_board(dev, board); + if (ret) + return ret; + } + ret = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); if (ret) goto out; @@ -845,17 +986,18 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id) } fb->dev = dev; + fb->vendor = vendor; fb->board = board; - dev_info(&fb->dev->dev, "PL%03x rev%u at 0x%08llx\n", - amba_part(dev), amba_rev(dev), + dev_info(&fb->dev->dev, "PL%03x designer %02x rev%u at 0x%08llx\n", + amba_part(dev), amba_manf(dev), amba_rev(dev), (unsigned long long)dev->res.start); ret = fb->board->setup(fb); if (ret) goto free_fb; - ret = clcdfb_register(fb); + ret = clcdfb_register(fb); if (ret == 0) { amba_set_drvdata(dev, fb); goto out; @@ -891,10 +1033,30 @@ static int clcdfb_remove(struct amba_device *dev) return 0; } +static struct clcd_vendor_data vendor_arm = { + /* Sets up the versatile board displays */ + .init_panel = versatile_clcd_init_panel, +}; + +static struct clcd_vendor_data vendor_nomadik = { + .clock_timregs = true, + .packed_24_bit_pixels = true, + .st_bitmux_control = true, + .init_board = nomadik_clcd_init_board, + .init_panel = nomadik_clcd_init_panel, +}; + static struct amba_id clcdfb_id_table[] = { { .id = 0x00041110, .mask = 0x000ffffe, + .data = &vendor_arm, + }, + /* ST Electronics Nomadik variant */ + { + .id = 0x00180110, + .mask = 0x00fffffe, + .data = &vendor_nomadik, }, { 0, 0 }, }; diff --git a/drivers/video/fbdev/arcfb.c b/drivers/video/fbdev/arcfb.c index 1b0b233b8b39..1928cb2b5386 100644 --- a/drivers/video/fbdev/arcfb.c +++ b/drivers/video/fbdev/arcfb.c @@ -79,7 +79,7 @@ struct arcfb_par { spinlock_t lock; }; -static struct fb_fix_screeninfo arcfb_fix = { +static const struct fb_fix_screeninfo arcfb_fix = { .id = "arcfb", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_MONO01, @@ -89,7 +89,7 @@ static struct fb_fix_screeninfo arcfb_fix = { .accel = FB_ACCEL_NONE, }; -static struct fb_var_screeninfo arcfb_var = { +static const struct fb_var_screeninfo arcfb_var = { .xres = 128, .yres = 64, .xres_virtual = 128, diff --git a/drivers/video/fbdev/asiliantfb.c b/drivers/video/fbdev/asiliantfb.c index 7e8ddf00ccc2..91eea4583382 100644 --- a/drivers/video/fbdev/asiliantfb.c +++ b/drivers/video/fbdev/asiliantfb.c @@ -474,7 +474,7 @@ static void chips_hw_init(struct fb_info *p) write_fr(chips_init_fr[i].addr, chips_init_fr[i].data); } -static struct fb_fix_screeninfo asiliantfb_fix = { +static const struct fb_fix_screeninfo asiliantfb_fix = { .id = "Asiliant 69000", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_PSEUDOCOLOR, @@ -483,7 +483,7 @@ static struct fb_fix_screeninfo asiliantfb_fix = { .smem_len = 0x200000, /* 2MB */ }; -static struct fb_var_screeninfo asiliantfb_var = { +static const struct fb_var_screeninfo asiliantfb_var = { .xres = 640, .yres = 480, .xres_virtual = 640, diff --git a/drivers/video/fbdev/aty/aty128fb.c b/drivers/video/fbdev/aty/aty128fb.c index 0a4626886b00..fa07242a78d2 100644 --- a/drivers/video/fbdev/aty/aty128fb.c +++ b/drivers/video/fbdev/aty/aty128fb.c @@ -93,7 +93,7 @@ #ifndef CONFIG_PPC_PMAC /* default mode */ -static struct fb_var_screeninfo default_var = { +static const struct fb_var_screeninfo default_var = { /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */ 640, 480, 640, 480, 0, 0, 8, 0, {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, @@ -104,7 +104,7 @@ static struct fb_var_screeninfo default_var = { #else /* CONFIG_PPC_PMAC */ /* default to 1024x768 at 75Hz on PPC - this will work * on the iMac, the usual 640x480 @ 60Hz doesn't. */ -static struct fb_var_screeninfo default_var = { +static const struct fb_var_screeninfo default_var = { /* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */ 1024, 768, 1024, 768, 0, 0, 8, 0, {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0}, @@ -375,7 +375,7 @@ static const struct aty128_meminfo ddr_sgram = { .name = "64-bit DDR SGRAM", }; -static struct fb_fix_screeninfo aty128fb_fix = { +static const struct fb_fix_screeninfo aty128fb_fix = { .id = "ATY Rage128", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_PSEUDOCOLOR, diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c index f34ed47fcaf8..11026e726b68 100644 --- a/drivers/video/fbdev/aty/atyfb_base.c +++ b/drivers/video/fbdev/aty/atyfb_base.c @@ -212,7 +212,7 @@ struct pci_mmap_map { unsigned long prot_mask; }; -static struct fb_fix_screeninfo atyfb_fix = { +static const struct fb_fix_screeninfo atyfb_fix = { .id = "ATY Mach64", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_PSEUDOCOLOR, diff --git a/drivers/video/fbdev/aty/radeon_monitor.c b/drivers/video/fbdev/aty/radeon_monitor.c index f1ce229de78d..278b421ab3fe 100644 --- a/drivers/video/fbdev/aty/radeon_monitor.c +++ b/drivers/video/fbdev/aty/radeon_monitor.c @@ -4,7 +4,7 @@ #include "../edid.h" -static struct fb_var_screeninfo radeonfb_default_var = { +static const struct fb_var_screeninfo radeonfb_default_var = { .xres = 640, .yres = 480, .xres_virtual = 640, diff --git a/drivers/video/fbdev/au1200fb.c b/drivers/video/fbdev/au1200fb.c index f9507b1894df..6c2b2ca4a909 100644 --- a/drivers/video/fbdev/au1200fb.c +++ b/drivers/video/fbdev/au1200fb.c @@ -43,6 +43,7 @@ #include <linux/ctype.h> #include <linux/dma-mapping.h> #include <linux/slab.h> +#include <linux/uaccess.h> #include <asm/mach-au1x00/au1000.h> #include <asm/mach-au1x00/au1200fb.h> /* platform_data */ diff --git a/drivers/video/fbdev/bfin_adv7393fb.c b/drivers/video/fbdev/bfin_adv7393fb.c index e2d7d039ce3b..542ffaddc6ab 100644 --- a/drivers/video/fbdev/bfin_adv7393fb.c +++ b/drivers/video/fbdev/bfin_adv7393fb.c @@ -375,7 +375,6 @@ static int bfin_adv7393_fb_probe(struct i2c_client *client, { int ret = 0; struct proc_dir_entry *entry; - int num_modes = ARRAY_SIZE(known_modes); struct adv7393fb_device *fbdev = NULL; @@ -384,7 +383,7 @@ static int bfin_adv7393_fb_probe(struct i2c_client *client, return -EINVAL; } - if (mode > num_modes) { + if (mode >= ARRAY_SIZE(known_modes)) { dev_err(&client->dev, "mode %d: not supported", mode); return -EFAULT; } @@ -797,7 +796,7 @@ static struct i2c_driver bfin_adv7393_fb_driver = { static int __init bfin_adv7393_fb_driver_init(void) { -#if defined(CONFIG_I2C_BLACKFIN_TWI) || defined(CONFIG_I2C_BLACKFIN_TWI_MODULE) +#if IS_ENABLED(CONFIG_I2C_BLACKFIN_TWI) request_module("i2c-bfin-twi"); #else request_module("i2c-gpio"); diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c index 924bad45c176..37a37c4d04cb 100644 --- a/drivers/video/fbdev/efifb.c +++ b/drivers/video/fbdev/efifb.c @@ -50,9 +50,9 @@ static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green, return 1; if (regno < 16) { - red >>= 8; - green >>= 8; - blue >>= 8; + red >>= 16 - info->var.red.length; + green >>= 16 - info->var.green.length; + blue >>= 16 - info->var.blue.length; ((u32 *)(info->pseudo_palette))[regno] = (red << info->var.red.offset) | (green << info->var.green.offset) | diff --git a/drivers/video/fbdev/exynos/Kconfig b/drivers/video/fbdev/exynos/Kconfig deleted file mode 100644 index d916bef94f25..000000000000 --- a/drivers/video/fbdev/exynos/Kconfig +++ /dev/null @@ -1,32 +0,0 @@ -# -# Exynos Video configuration -# - -menuconfig EXYNOS_VIDEO - tristate "Exynos Video driver support" - depends on ARCH_S5PV210 || ARCH_EXYNOS - help - This enables support for EXYNOS Video device. - -if EXYNOS_VIDEO - -# -# MIPI DSI driver -# - -config EXYNOS_MIPI_DSI - tristate "EXYNOS MIPI DSI driver support." - select GENERIC_PHY - help - This enables support for MIPI-DSI device. - -config EXYNOS_LCD_S6E8AX0 - tristate "S6E8AX0 MIPI AMOLED LCD Driver" - depends on EXYNOS_MIPI_DSI && BACKLIGHT_CLASS_DEVICE - depends on (LCD_CLASS_DEVICE = y) - default n - help - If you have an S6E8AX0 MIPI AMOLED LCD Panel, say Y to enable its - LCD control driver. - -endif # EXYNOS_VIDEO diff --git a/drivers/video/fbdev/exynos/Makefile b/drivers/video/fbdev/exynos/Makefile deleted file mode 100644 index 02d8dc522fea..000000000000 --- a/drivers/video/fbdev/exynos/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -# -# Makefile for the exynos video drivers. -# - -obj-$(CONFIG_EXYNOS_MIPI_DSI) += exynos-mipi-dsi-mod.o - -exynos-mipi-dsi-mod-objs += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \ - exynos_mipi_dsi_lowlevel.o -obj-$(CONFIG_EXYNOS_LCD_S6E8AX0) += s6e8ax0.o diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi.c b/drivers/video/fbdev/exynos/exynos_mipi_dsi.c deleted file mode 100644 index 92e4af3caaf8..000000000000 --- a/drivers/video/fbdev/exynos/exynos_mipi_dsi.c +++ /dev/null @@ -1,574 +0,0 @@ -/* linux/drivers/video/exynos/exynos_mipi_dsi.c - * - * Samsung SoC MIPI-DSIM driver. - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd - * - * InKi Dae, <inki.dae@samsung.com> - * Donghwa Lee, <dh09.lee@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/clk.h> -#include <linux/mutex.h> -#include <linux/wait.h> -#include <linux/fs.h> -#include <linux/mm.h> -#include <linux/fb.h> -#include <linux/ctype.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/memory.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/kthread.h> -#include <linux/notifier.h> -#include <linux/phy/phy.h> -#include <linux/regulator/consumer.h> -#include <linux/pm_runtime.h> -#include <linux/err.h> - -#include <video/exynos_mipi_dsim.h> - -#include "exynos_mipi_dsi_common.h" -#include "exynos_mipi_dsi_lowlevel.h" - -struct mipi_dsim_ddi { - int bus_id; - struct list_head list; - struct mipi_dsim_lcd_device *dsim_lcd_dev; - struct mipi_dsim_lcd_driver *dsim_lcd_drv; -}; - -static LIST_HEAD(dsim_ddi_list); - -static DEFINE_MUTEX(mipi_dsim_lock); - -static struct mipi_dsim_platform_data *to_dsim_plat(struct platform_device - *pdev) -{ - return pdev->dev.platform_data; -} - -static struct regulator_bulk_data supplies[] = { - { .supply = "vdd11", }, - { .supply = "vdd18", }, -}; - -static int exynos_mipi_regulator_enable(struct mipi_dsim_device *dsim) -{ - int ret; - - mutex_lock(&dsim->lock); - ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); - mutex_unlock(&dsim->lock); - - return ret; -} - -static int exynos_mipi_regulator_disable(struct mipi_dsim_device *dsim) -{ - int ret; - - mutex_lock(&dsim->lock); - ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); - mutex_unlock(&dsim->lock); - - return ret; -} - -/* update all register settings to MIPI DSI controller. */ -static void exynos_mipi_update_cfg(struct mipi_dsim_device *dsim) -{ - /* - * data from Display controller(FIMD) is not transferred in video mode - * but in case of command mode, all settings is not updated to - * registers. - */ - exynos_mipi_dsi_stand_by(dsim, 0); - - exynos_mipi_dsi_init_dsim(dsim); - exynos_mipi_dsi_init_link(dsim); - - exynos_mipi_dsi_set_hs_enable(dsim); - - /* set display timing. */ - exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config); - - exynos_mipi_dsi_init_interrupt(dsim); - - /* - * data from Display controller(FIMD) is transferred in video mode - * but in case of command mode, all settings are updated to registers. - */ - exynos_mipi_dsi_stand_by(dsim, 1); -} - -static int exynos_mipi_dsi_early_blank_mode(struct mipi_dsim_device *dsim, - int power) -{ - struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; - struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; - - switch (power) { - case FB_BLANK_POWERDOWN: - if (dsim->suspended) - return 0; - - if (client_drv && client_drv->suspend) - client_drv->suspend(client_dev); - - clk_disable(dsim->clock); - - exynos_mipi_regulator_disable(dsim); - - dsim->suspended = true; - - break; - default: - break; - } - - return 0; -} - -static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power) -{ - struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; - struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; - - switch (power) { - case FB_BLANK_UNBLANK: - if (!dsim->suspended) - return 0; - - /* lcd panel power on. */ - if (client_drv && client_drv->power_on) - client_drv->power_on(client_dev, 1); - - exynos_mipi_regulator_enable(dsim); - - /* enable MIPI-DSI PHY. */ - phy_power_on(dsim->phy); - - clk_enable(dsim->clock); - - exynos_mipi_update_cfg(dsim); - - /* set lcd panel sequence commands. */ - if (client_drv && client_drv->set_sequence) - client_drv->set_sequence(client_dev); - - dsim->suspended = false; - - break; - case FB_BLANK_NORMAL: - /* TODO. */ - break; - default: - break; - } - - return 0; -} - -int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev) -{ - struct mipi_dsim_ddi *dsim_ddi; - - if (!lcd_dev->name) { - pr_err("dsim_lcd_device name is NULL.\n"); - return -EFAULT; - } - - dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL); - if (!dsim_ddi) { - pr_err("failed to allocate dsim_ddi object.\n"); - return -ENOMEM; - } - - dsim_ddi->dsim_lcd_dev = lcd_dev; - - mutex_lock(&mipi_dsim_lock); - list_add_tail(&dsim_ddi->list, &dsim_ddi_list); - mutex_unlock(&mipi_dsim_lock); - - return 0; -} - -static struct mipi_dsim_ddi *exynos_mipi_dsi_find_lcd_device( - struct mipi_dsim_lcd_driver *lcd_drv) -{ - struct mipi_dsim_ddi *dsim_ddi, *next; - struct mipi_dsim_lcd_device *lcd_dev; - - mutex_lock(&mipi_dsim_lock); - - list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) { - if (!dsim_ddi) - goto out; - - lcd_dev = dsim_ddi->dsim_lcd_dev; - if (!lcd_dev) - continue; - - if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0) { - /** - * bus_id would be used to identify - * connected bus. - */ - dsim_ddi->bus_id = lcd_dev->bus_id; - mutex_unlock(&mipi_dsim_lock); - - return dsim_ddi; - } - - list_del(&dsim_ddi->list); - kfree(dsim_ddi); - } - -out: - mutex_unlock(&mipi_dsim_lock); - - return NULL; -} - -int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver *lcd_drv) -{ - struct mipi_dsim_ddi *dsim_ddi; - - if (!lcd_drv->name) { - pr_err("dsim_lcd_driver name is NULL.\n"); - return -EFAULT; - } - - dsim_ddi = exynos_mipi_dsi_find_lcd_device(lcd_drv); - if (!dsim_ddi) { - pr_err("mipi_dsim_ddi object not found.\n"); - return -EFAULT; - } - - dsim_ddi->dsim_lcd_drv = lcd_drv; - - pr_info("registered panel driver(%s) to mipi-dsi driver.\n", - lcd_drv->name); - - return 0; - -} -EXPORT_SYMBOL_GPL(exynos_mipi_dsi_register_lcd_driver); - -static struct mipi_dsim_ddi *exynos_mipi_dsi_bind_lcd_ddi( - struct mipi_dsim_device *dsim, - const char *name) -{ - struct mipi_dsim_ddi *dsim_ddi, *next; - struct mipi_dsim_lcd_driver *lcd_drv; - struct mipi_dsim_lcd_device *lcd_dev; - int ret; - - mutex_lock(&dsim->lock); - - list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) { - lcd_drv = dsim_ddi->dsim_lcd_drv; - lcd_dev = dsim_ddi->dsim_lcd_dev; - if (!lcd_drv || !lcd_dev || - (dsim->id != dsim_ddi->bus_id)) - continue; - - dev_dbg(dsim->dev, "lcd_drv->id = %d, lcd_dev->id = %d\n", - lcd_drv->id, lcd_dev->id); - dev_dbg(dsim->dev, "lcd_dev->bus_id = %d, dsim->id = %d\n", - lcd_dev->bus_id, dsim->id); - - if ((strcmp(lcd_drv->name, name) == 0)) { - lcd_dev->master = dsim; - - lcd_dev->dev.parent = dsim->dev; - dev_set_name(&lcd_dev->dev, "%s", lcd_drv->name); - - ret = device_register(&lcd_dev->dev); - if (ret < 0) { - dev_err(dsim->dev, - "can't register %s, status %d\n", - dev_name(&lcd_dev->dev), ret); - mutex_unlock(&dsim->lock); - - return NULL; - } - - dsim->dsim_lcd_dev = lcd_dev; - dsim->dsim_lcd_drv = lcd_drv; - - mutex_unlock(&dsim->lock); - - return dsim_ddi; - } - } - - mutex_unlock(&dsim->lock); - - return NULL; -} - -/* define MIPI-DSI Master operations. */ -static struct mipi_dsim_master_ops master_ops = { - .cmd_read = exynos_mipi_dsi_rd_data, - .cmd_write = exynos_mipi_dsi_wr_data, - .get_dsim_frame_done = exynos_mipi_dsi_get_frame_done_status, - .clear_dsim_frame_done = exynos_mipi_dsi_clear_frame_done, - .set_early_blank_mode = exynos_mipi_dsi_early_blank_mode, - .set_blank_mode = exynos_mipi_dsi_blank_mode, -}; - -static int exynos_mipi_dsi_probe(struct platform_device *pdev) -{ - struct resource *res; - struct mipi_dsim_device *dsim; - struct mipi_dsim_config *dsim_config; - struct mipi_dsim_platform_data *dsim_pd; - struct mipi_dsim_ddi *dsim_ddi; - int ret = -EINVAL; - - dsim = devm_kzalloc(&pdev->dev, sizeof(struct mipi_dsim_device), - GFP_KERNEL); - if (!dsim) { - dev_err(&pdev->dev, "failed to allocate dsim object.\n"); - return -ENOMEM; - } - - dsim->pd = to_dsim_plat(pdev); - dsim->dev = &pdev->dev; - dsim->id = pdev->id; - - /* get mipi_dsim_platform_data. */ - dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd; - if (dsim_pd == NULL) { - dev_err(&pdev->dev, "failed to get platform data for dsim.\n"); - return -EINVAL; - } - /* get mipi_dsim_config. */ - dsim_config = dsim_pd->dsim_config; - if (dsim_config == NULL) { - dev_err(&pdev->dev, "failed to get dsim config data.\n"); - return -EINVAL; - } - - dsim->dsim_config = dsim_config; - dsim->master_ops = &master_ops; - - mutex_init(&dsim->lock); - - ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(supplies), - supplies); - if (ret) { - dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret); - return ret; - } - - dsim->phy = devm_phy_get(&pdev->dev, "dsim"); - if (IS_ERR(dsim->phy)) - return PTR_ERR(dsim->phy); - - dsim->clock = devm_clk_get(&pdev->dev, "dsim0"); - if (IS_ERR(dsim->clock)) { - dev_err(&pdev->dev, "failed to get dsim clock source\n"); - return -ENODEV; - } - - clk_enable(dsim->clock); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - dsim->reg_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dsim->reg_base)) { - ret = PTR_ERR(dsim->reg_base); - goto error; - } - - mutex_init(&dsim->lock); - - /* bind lcd ddi matched with panel name. */ - dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, dsim_pd->lcd_panel_name); - if (!dsim_ddi) { - dev_err(&pdev->dev, "mipi_dsim_ddi object not found.\n"); - ret = -EINVAL; - goto error; - } - - ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request dsim irq resource\n"); - goto error; - } - dsim->irq = ret; - - init_completion(&dsim_wr_comp); - init_completion(&dsim_rd_comp); - platform_set_drvdata(pdev, dsim); - - ret = devm_request_irq(&pdev->dev, dsim->irq, - exynos_mipi_dsi_interrupt_handler, - IRQF_SHARED, dev_name(&pdev->dev), dsim); - if (ret != 0) { - dev_err(&pdev->dev, "failed to request dsim irq\n"); - ret = -EINVAL; - goto error; - } - - /* enable interrupts */ - exynos_mipi_dsi_init_interrupt(dsim); - - /* initialize mipi-dsi client(lcd panel). */ - if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->probe) - dsim_ddi->dsim_lcd_drv->probe(dsim_ddi->dsim_lcd_dev); - - /* in case mipi-dsi has been enabled by bootloader */ - if (dsim_pd->enabled) { - exynos_mipi_regulator_enable(dsim); - goto done; - } - - /* lcd panel power on. */ - if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->power_on) - dsim_ddi->dsim_lcd_drv->power_on(dsim_ddi->dsim_lcd_dev, 1); - - exynos_mipi_regulator_enable(dsim); - - /* enable MIPI-DSI PHY. */ - phy_power_on(dsim->phy); - - exynos_mipi_update_cfg(dsim); - - /* set lcd panel sequence commands. */ - if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->set_sequence) - dsim_ddi->dsim_lcd_drv->set_sequence(dsim_ddi->dsim_lcd_dev); - - dsim->suspended = false; - -done: - platform_set_drvdata(pdev, dsim); - - dev_dbg(&pdev->dev, "%s() completed successfully (%s mode)\n", __func__, - dsim_config->e_interface == DSIM_COMMAND ? "CPU" : "RGB"); - - return 0; - -error: - clk_disable(dsim->clock); - return ret; -} - -static int exynos_mipi_dsi_remove(struct platform_device *pdev) -{ - struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); - struct mipi_dsim_ddi *dsim_ddi, *next; - struct mipi_dsim_lcd_driver *dsim_lcd_drv; - - clk_disable(dsim->clock); - - list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) { - if (dsim_ddi) { - if (dsim->id != dsim_ddi->bus_id) - continue; - - dsim_lcd_drv = dsim_ddi->dsim_lcd_drv; - - if (dsim_lcd_drv->remove) - dsim_lcd_drv->remove(dsim_ddi->dsim_lcd_dev); - - kfree(dsim_ddi); - } - } - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int exynos_mipi_dsi_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); - struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; - struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; - - disable_irq(dsim->irq); - - if (dsim->suspended) - return 0; - - if (client_drv && client_drv->suspend) - client_drv->suspend(client_dev); - - /* disable MIPI-DSI PHY. */ - phy_power_off(dsim->phy); - - clk_disable(dsim->clock); - - exynos_mipi_regulator_disable(dsim); - - dsim->suspended = true; - - return 0; -} - -static int exynos_mipi_dsi_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct mipi_dsim_device *dsim = platform_get_drvdata(pdev); - struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv; - struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev; - - enable_irq(dsim->irq); - - if (!dsim->suspended) - return 0; - - /* lcd panel power on. */ - if (client_drv && client_drv->power_on) - client_drv->power_on(client_dev, 1); - - exynos_mipi_regulator_enable(dsim); - - /* enable MIPI-DSI PHY. */ - phy_power_on(dsim->phy); - - clk_enable(dsim->clock); - - exynos_mipi_update_cfg(dsim); - - /* set lcd panel sequence commands. */ - if (client_drv && client_drv->set_sequence) - client_drv->set_sequence(client_dev); - - dsim->suspended = false; - - return 0; -} -#endif - -static const struct dev_pm_ops exynos_mipi_dsi_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(exynos_mipi_dsi_suspend, exynos_mipi_dsi_resume) -}; - -static struct platform_driver exynos_mipi_dsi_driver = { - .probe = exynos_mipi_dsi_probe, - .remove = exynos_mipi_dsi_remove, - .driver = { - .name = "exynos-mipi-dsim", - .pm = &exynos_mipi_dsi_pm_ops, - }, -}; - -module_platform_driver(exynos_mipi_dsi_driver); - -MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>"); -MODULE_DESCRIPTION("Samsung SoC MIPI-DSI driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.c b/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.c deleted file mode 100644 index 2358a2fbbbcd..000000000000 --- a/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.c +++ /dev/null @@ -1,880 +0,0 @@ -/* linux/drivers/video/exynos/exynos_mipi_dsi_common.c - * - * Samsung SoC MIPI-DSI common driver. - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd - * - * InKi Dae, <inki.dae@samsung.com> - * Donghwa Lee, <dh09.lee@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/mutex.h> -#include <linux/wait.h> -#include <linux/fs.h> -#include <linux/mm.h> -#include <linux/fb.h> -#include <linux/ctype.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/memory.h> -#include <linux/delay.h> -#include <linux/irqreturn.h> -#include <linux/kthread.h> - -#include <video/mipi_display.h> -#include <video/exynos_mipi_dsim.h> - -#include "exynos_mipi_dsi_regs.h" -#include "exynos_mipi_dsi_lowlevel.h" -#include "exynos_mipi_dsi_common.h" - -#define MIPI_FIFO_TIMEOUT msecs_to_jiffies(250) -#define MIPI_RX_FIFO_READ_DONE 0x30800002 -#define MIPI_MAX_RX_FIFO 20 -#define MHZ (1000 * 1000) -#define FIN_HZ (24 * MHZ) - -#define DFIN_PLL_MIN_HZ (6 * MHZ) -#define DFIN_PLL_MAX_HZ (12 * MHZ) - -#define DFVCO_MIN_HZ (500 * MHZ) -#define DFVCO_MAX_HZ (1000 * MHZ) - -#define TRY_GET_FIFO_TIMEOUT (5000 * 2) -#define TRY_FIFO_CLEAR (10) - -/* MIPI-DSIM status types. */ -enum { - DSIM_STATE_INIT, /* should be initialized. */ - DSIM_STATE_STOP, /* CPU and LCDC are LP mode. */ - DSIM_STATE_HSCLKEN, /* HS clock was enabled. */ - DSIM_STATE_ULPS -}; - -/* define DSI lane types. */ -enum { - DSIM_LANE_CLOCK = (1 << 0), - DSIM_LANE_DATA0 = (1 << 1), - DSIM_LANE_DATA1 = (1 << 2), - DSIM_LANE_DATA2 = (1 << 3), - DSIM_LANE_DATA3 = (1 << 4) -}; - -static unsigned int dpll_table[15] = { - 100, 120, 170, 220, 270, - 320, 390, 450, 510, 560, - 640, 690, 770, 870, 950 -}; - -irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id) -{ - struct mipi_dsim_device *dsim = dev_id; - unsigned int intsrc, intmsk; - - intsrc = exynos_mipi_dsi_read_interrupt(dsim); - intmsk = exynos_mipi_dsi_read_interrupt_mask(dsim); - intmsk = ~intmsk & intsrc; - - if (intsrc & INTMSK_RX_DONE) { - complete(&dsim_rd_comp); - dev_dbg(dsim->dev, "MIPI INTMSK_RX_DONE\n"); - } - if (intsrc & INTMSK_FIFO_EMPTY) { - complete(&dsim_wr_comp); - dev_dbg(dsim->dev, "MIPI INTMSK_FIFO_EMPTY\n"); - } - - exynos_mipi_dsi_clear_interrupt(dsim, intmsk); - - return IRQ_HANDLED; -} - -/* - * write long packet to mipi dsi slave - * @dsim: mipi dsim device structure. - * @data0: packet data to send. - * @data1: size of packet data - */ -static void exynos_mipi_dsi_long_data_wr(struct mipi_dsim_device *dsim, - const unsigned char *data0, unsigned int data_size) -{ - unsigned int data_cnt = 0, payload = 0; - - /* in case that data count is more then 4 */ - for (data_cnt = 0; data_cnt < data_size; data_cnt += 4) { - /* - * after sending 4bytes per one time, - * send remainder data less then 4. - */ - if ((data_size - data_cnt) < 4) { - if ((data_size - data_cnt) == 3) { - payload = data0[data_cnt] | - data0[data_cnt + 1] << 8 | - data0[data_cnt + 2] << 16; - dev_dbg(dsim->dev, "count = 3 payload = %x, %x %x %x\n", - payload, data0[data_cnt], - data0[data_cnt + 1], - data0[data_cnt + 2]); - } else if ((data_size - data_cnt) == 2) { - payload = data0[data_cnt] | - data0[data_cnt + 1] << 8; - dev_dbg(dsim->dev, - "count = 2 payload = %x, %x %x\n", payload, - data0[data_cnt], - data0[data_cnt + 1]); - } else if ((data_size - data_cnt) == 1) { - payload = data0[data_cnt]; - } - - exynos_mipi_dsi_wr_tx_data(dsim, payload); - /* send 4bytes per one time. */ - } else { - payload = data0[data_cnt] | - data0[data_cnt + 1] << 8 | - data0[data_cnt + 2] << 16 | - data0[data_cnt + 3] << 24; - - dev_dbg(dsim->dev, - "count = 4 payload = %x, %x %x %x %x\n", - payload, *(u8 *)(data0 + data_cnt), - data0[data_cnt + 1], - data0[data_cnt + 2], - data0[data_cnt + 3]); - - exynos_mipi_dsi_wr_tx_data(dsim, payload); - } - } -} - -int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id, - const unsigned char *data0, unsigned int data_size) -{ - unsigned int check_rx_ack = 0; - - if (dsim->state == DSIM_STATE_ULPS) { - dev_err(dsim->dev, "state is ULPS.\n"); - - return -EINVAL; - } - - /* FIXME!!! why does it need this delay? */ - msleep(20); - - mutex_lock(&dsim->lock); - - switch (data_id) { - /* short packet types of packet types for command. */ - case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: - case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: - case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: - case MIPI_DSI_DCS_SHORT_WRITE: - case MIPI_DSI_DCS_SHORT_WRITE_PARAM: - case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE: - exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]); - if (check_rx_ack) { - /* process response func should be implemented */ - mutex_unlock(&dsim->lock); - return 0; - } else { - mutex_unlock(&dsim->lock); - return -EINVAL; - } - - /* general command */ - case MIPI_DSI_COLOR_MODE_OFF: - case MIPI_DSI_COLOR_MODE_ON: - case MIPI_DSI_SHUTDOWN_PERIPHERAL: - case MIPI_DSI_TURN_ON_PERIPHERAL: - exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]); - if (check_rx_ack) { - /* process response func should be implemented. */ - mutex_unlock(&dsim->lock); - return 0; - } else { - mutex_unlock(&dsim->lock); - return -EINVAL; - } - - /* packet types for video data */ - case MIPI_DSI_V_SYNC_START: - case MIPI_DSI_V_SYNC_END: - case MIPI_DSI_H_SYNC_START: - case MIPI_DSI_H_SYNC_END: - case MIPI_DSI_END_OF_TRANSMISSION: - mutex_unlock(&dsim->lock); - return 0; - - /* long packet type and null packet */ - case MIPI_DSI_NULL_PACKET: - case MIPI_DSI_BLANKING_PACKET: - mutex_unlock(&dsim->lock); - return 0; - case MIPI_DSI_GENERIC_LONG_WRITE: - case MIPI_DSI_DCS_LONG_WRITE: - { - unsigned int size, payload = 0; - reinit_completion(&dsim_wr_comp); - - size = data_size * 4; - - /* if data count is less then 4, then send 3bytes data. */ - if (data_size < 4) { - payload = data0[0] | - data0[1] << 8 | - data0[2] << 16; - - exynos_mipi_dsi_wr_tx_data(dsim, payload); - - dev_dbg(dsim->dev, "count = %d payload = %x,%x %x %x\n", - data_size, payload, data0[0], - data0[1], data0[2]); - - /* in case that data count is more then 4 */ - } else - exynos_mipi_dsi_long_data_wr(dsim, data0, data_size); - - /* put data into header fifo */ - exynos_mipi_dsi_wr_tx_header(dsim, data_id, data_size & 0xff, - (data_size & 0xff00) >> 8); - - if (!wait_for_completion_interruptible_timeout(&dsim_wr_comp, - MIPI_FIFO_TIMEOUT)) { - dev_warn(dsim->dev, "command write timeout.\n"); - mutex_unlock(&dsim->lock); - return -EAGAIN; - } - - if (check_rx_ack) { - /* process response func should be implemented. */ - mutex_unlock(&dsim->lock); - return 0; - } else { - mutex_unlock(&dsim->lock); - return -EINVAL; - } - } - - /* packet typo for video data */ - case MIPI_DSI_PACKED_PIXEL_STREAM_16: - case MIPI_DSI_PACKED_PIXEL_STREAM_18: - case MIPI_DSI_PIXEL_STREAM_3BYTE_18: - case MIPI_DSI_PACKED_PIXEL_STREAM_24: - if (check_rx_ack) { - /* process response func should be implemented. */ - mutex_unlock(&dsim->lock); - return 0; - } else { - mutex_unlock(&dsim->lock); - return -EINVAL; - } - default: - dev_warn(dsim->dev, - "data id %x is not supported current DSI spec.\n", - data_id); - - mutex_unlock(&dsim->lock); - return -EINVAL; - } -} - -static unsigned int exynos_mipi_dsi_long_data_rd(struct mipi_dsim_device *dsim, - unsigned int req_size, unsigned int rx_data, u8 *rx_buf) -{ - unsigned int rcv_pkt, i, j; - u16 rxsize; - - /* for long packet */ - rxsize = (u16)((rx_data & 0x00ffff00) >> 8); - dev_dbg(dsim->dev, "mipi dsi rx size : %d\n", rxsize); - if (rxsize != req_size) { - dev_dbg(dsim->dev, - "received size mismatch received: %d, requested: %d\n", - rxsize, req_size); - goto err; - } - - for (i = 0; i < (rxsize >> 2); i++) { - rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); - dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt); - for (j = 0; j < 4; j++) { - rx_buf[(i * 4) + j] = - (u8)(rcv_pkt >> (j * 8)) & 0xff; - dev_dbg(dsim->dev, "received value : %02x\n", - (rcv_pkt >> (j * 8)) & 0xff); - } - } - if (rxsize % 4) { - rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); - dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt); - for (j = 0; j < (rxsize % 4); j++) { - rx_buf[(i * 4) + j] = - (u8)(rcv_pkt >> (j * 8)) & 0xff; - dev_dbg(dsim->dev, "received value : %02x\n", - (rcv_pkt >> (j * 8)) & 0xff); - } - } - - return rxsize; - -err: - return -EINVAL; -} - -static unsigned int exynos_mipi_dsi_response_size(unsigned int req_size) -{ - switch (req_size) { - case 1: - return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE; - case 2: - return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE; - default: - return MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE; - } -} - -int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id, - unsigned int data0, unsigned int req_size, u8 *rx_buf) -{ - unsigned int rx_data, rcv_pkt, i; - u8 response = 0; - u16 rxsize; - - if (dsim->state == DSIM_STATE_ULPS) { - dev_err(dsim->dev, "state is ULPS.\n"); - - return -EINVAL; - } - - /* FIXME!!! */ - msleep(20); - - mutex_lock(&dsim->lock); - reinit_completion(&dsim_rd_comp); - exynos_mipi_dsi_rd_tx_header(dsim, - MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, req_size); - - response = exynos_mipi_dsi_response_size(req_size); - - switch (data_id) { - case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: - case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: - case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: - case MIPI_DSI_DCS_READ: - exynos_mipi_dsi_rd_tx_header(dsim, - data_id, data0); - /* process response func should be implemented. */ - break; - default: - dev_warn(dsim->dev, - "data id %x is not supported current DSI spec.\n", - data_id); - - mutex_unlock(&dsim->lock); - return -EINVAL; - } - - if (!wait_for_completion_interruptible_timeout(&dsim_rd_comp, - MIPI_FIFO_TIMEOUT)) { - pr_err("RX done interrupt timeout\n"); - mutex_unlock(&dsim->lock); - return 0; - } - - msleep(20); - - rx_data = exynos_mipi_dsi_rd_rx_fifo(dsim); - - if ((u8)(rx_data & 0xff) != response) { - printk(KERN_ERR - "mipi dsi wrong response rx_data : %x, response:%x\n", - rx_data, response); - goto clear_rx_fifo; - } - - if (req_size <= 2) { - /* for short packet */ - for (i = 0; i < req_size; i++) - rx_buf[i] = (rx_data >> (8 + (i * 8))) & 0xff; - rxsize = req_size; - } else { - /* for long packet */ - rxsize = exynos_mipi_dsi_long_data_rd(dsim, req_size, rx_data, - rx_buf); - if (rxsize != req_size) - goto clear_rx_fifo; - } - - rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); - - msleep(20); - - if (rcv_pkt != MIPI_RX_FIFO_READ_DONE) { - dev_info(dsim->dev, - "Can't found RX FIFO READ DONE FLAG : %x\n", rcv_pkt); - goto clear_rx_fifo; - } - - mutex_unlock(&dsim->lock); - - return rxsize; - -clear_rx_fifo: - i = 0; - while (1) { - rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim); - if ((rcv_pkt == MIPI_RX_FIFO_READ_DONE) - || (i > MIPI_MAX_RX_FIFO)) - break; - dev_dbg(dsim->dev, - "mipi dsi clear rx fifo : %08x\n", rcv_pkt); - i++; - } - dev_info(dsim->dev, - "mipi dsi rx done count : %d, rcv_pkt : %08x\n", i, rcv_pkt); - - mutex_unlock(&dsim->lock); - - return 0; -} - -static int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim, - unsigned int enable) -{ - int sw_timeout; - - if (enable) { - sw_timeout = 1000; - - exynos_mipi_dsi_enable_pll(dsim, 1); - while (1) { - sw_timeout--; - if (exynos_mipi_dsi_is_pll_stable(dsim)) - return 0; - if (sw_timeout == 0) - return -EINVAL; - } - } else - exynos_mipi_dsi_enable_pll(dsim, 0); - - return 0; -} - -static unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim, - unsigned int pre_divider, unsigned int main_divider, - unsigned int scaler) -{ - unsigned long dfin_pll, dfvco, dpll_out; - unsigned int i, freq_band = 0xf; - - dfin_pll = (FIN_HZ / pre_divider); - - /****************************************************** - * Serial Clock(=ByteClk X 8) FreqBand[3:0] * - ****************************************************** - * ~ 99.99 MHz 0000 - * 100 ~ 119.99 MHz 0001 - * 120 ~ 159.99 MHz 0010 - * 160 ~ 199.99 MHz 0011 - * 200 ~ 239.99 MHz 0100 - * 140 ~ 319.99 MHz 0101 - * 320 ~ 389.99 MHz 0110 - * 390 ~ 449.99 MHz 0111 - * 450 ~ 509.99 MHz 1000 - * 510 ~ 559.99 MHz 1001 - * 560 ~ 639.99 MHz 1010 - * 640 ~ 689.99 MHz 1011 - * 690 ~ 769.99 MHz 1100 - * 770 ~ 869.99 MHz 1101 - * 870 ~ 949.99 MHz 1110 - * 950 ~ 1000 MHz 1111 - ******************************************************/ - if (dfin_pll < DFIN_PLL_MIN_HZ || dfin_pll > DFIN_PLL_MAX_HZ) { - dev_warn(dsim->dev, "fin_pll range should be 6MHz ~ 12MHz\n"); - exynos_mipi_dsi_enable_afc(dsim, 0, 0); - } else { - if (dfin_pll < 7 * MHZ) - exynos_mipi_dsi_enable_afc(dsim, 1, 0x1); - else if (dfin_pll < 8 * MHZ) - exynos_mipi_dsi_enable_afc(dsim, 1, 0x0); - else if (dfin_pll < 9 * MHZ) - exynos_mipi_dsi_enable_afc(dsim, 1, 0x3); - else if (dfin_pll < 10 * MHZ) - exynos_mipi_dsi_enable_afc(dsim, 1, 0x2); - else if (dfin_pll < 11 * MHZ) - exynos_mipi_dsi_enable_afc(dsim, 1, 0x5); - else - exynos_mipi_dsi_enable_afc(dsim, 1, 0x4); - } - - dfvco = dfin_pll * main_divider; - dev_dbg(dsim->dev, "dfvco = %lu, dfin_pll = %lu, main_divider = %d\n", - dfvco, dfin_pll, main_divider); - if (dfvco < DFVCO_MIN_HZ || dfvco > DFVCO_MAX_HZ) - dev_warn(dsim->dev, "fvco range should be 500MHz ~ 1000MHz\n"); - - dpll_out = dfvco / (1 << scaler); - dev_dbg(dsim->dev, "dpll_out = %lu, dfvco = %lu, scaler = %d\n", - dpll_out, dfvco, scaler); - - for (i = 0; i < ARRAY_SIZE(dpll_table); i++) { - if (dpll_out < dpll_table[i] * MHZ) { - freq_band = i; - break; - } - } - - dev_dbg(dsim->dev, "freq_band = %d\n", freq_band); - - exynos_mipi_dsi_pll_freq(dsim, pre_divider, main_divider, scaler); - - exynos_mipi_dsi_hs_zero_ctrl(dsim, 0); - exynos_mipi_dsi_prep_ctrl(dsim, 0); - - /* Freq Band */ - exynos_mipi_dsi_pll_freq_band(dsim, freq_band); - - /* Stable time */ - exynos_mipi_dsi_pll_stable_time(dsim, dsim->dsim_config->pll_stable_time); - - /* Enable PLL */ - dev_dbg(dsim->dev, "FOUT of mipi dphy pll is %luMHz\n", - (dpll_out / MHZ)); - - return dpll_out; -} - -static int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim, - unsigned int byte_clk_sel, unsigned int enable) -{ - unsigned int esc_div; - unsigned long esc_clk_error_rate; - unsigned long hs_clk = 0, byte_clk = 0, escape_clk = 0; - - if (enable) { - dsim->e_clk_src = byte_clk_sel; - - /* Escape mode clock and byte clock source */ - exynos_mipi_dsi_set_byte_clock_src(dsim, byte_clk_sel); - - /* DPHY, DSIM Link : D-PHY clock out */ - if (byte_clk_sel == DSIM_PLL_OUT_DIV8) { - hs_clk = exynos_mipi_dsi_change_pll(dsim, - dsim->dsim_config->p, dsim->dsim_config->m, - dsim->dsim_config->s); - if (hs_clk == 0) { - dev_err(dsim->dev, - "failed to get hs clock.\n"); - return -EINVAL; - } - - byte_clk = hs_clk / 8; - exynos_mipi_dsi_enable_pll_bypass(dsim, 0); - exynos_mipi_dsi_pll_on(dsim, 1); - /* DPHY : D-PHY clock out, DSIM link : external clock out */ - } else if (byte_clk_sel == DSIM_EXT_CLK_DIV8) { - dev_warn(dsim->dev, "this project is not support\n"); - dev_warn(dsim->dev, - "external clock source for MIPI DSIM.\n"); - } else if (byte_clk_sel == DSIM_EXT_CLK_BYPASS) { - dev_warn(dsim->dev, "this project is not support\n"); - dev_warn(dsim->dev, - "external clock source for MIPI DSIM\n"); - } - - /* escape clock divider */ - esc_div = byte_clk / (dsim->dsim_config->esc_clk); - dev_dbg(dsim->dev, - "esc_div = %d, byte_clk = %lu, esc_clk = %lu\n", - esc_div, byte_clk, dsim->dsim_config->esc_clk); - if ((byte_clk / esc_div) >= (20 * MHZ) || - (byte_clk / esc_div) > - dsim->dsim_config->esc_clk) - esc_div += 1; - - escape_clk = byte_clk / esc_div; - dev_dbg(dsim->dev, - "escape_clk = %lu, byte_clk = %lu, esc_div = %d\n", - escape_clk, byte_clk, esc_div); - - /* enable escape clock. */ - exynos_mipi_dsi_enable_byte_clock(dsim, 1); - - /* enable byte clk and escape clock */ - exynos_mipi_dsi_set_esc_clk_prs(dsim, 1, esc_div); - /* escape clock on lane */ - exynos_mipi_dsi_enable_esc_clk_on_lane(dsim, - (DSIM_LANE_CLOCK | dsim->data_lane), 1); - - dev_dbg(dsim->dev, "byte clock is %luMHz\n", - (byte_clk / MHZ)); - dev_dbg(dsim->dev, "escape clock that user's need is %lu\n", - (dsim->dsim_config->esc_clk / MHZ)); - dev_dbg(dsim->dev, "escape clock divider is %x\n", esc_div); - dev_dbg(dsim->dev, "escape clock is %luMHz\n", - ((byte_clk / esc_div) / MHZ)); - - if ((byte_clk / esc_div) > escape_clk) { - esc_clk_error_rate = escape_clk / - (byte_clk / esc_div); - dev_warn(dsim->dev, "error rate is %lu over.\n", - (esc_clk_error_rate / 100)); - } else if ((byte_clk / esc_div) < (escape_clk)) { - esc_clk_error_rate = (byte_clk / esc_div) / - escape_clk; - dev_warn(dsim->dev, "error rate is %lu under.\n", - (esc_clk_error_rate / 100)); - } - } else { - exynos_mipi_dsi_enable_esc_clk_on_lane(dsim, - (DSIM_LANE_CLOCK | dsim->data_lane), 0); - exynos_mipi_dsi_set_esc_clk_prs(dsim, 0, 0); - - /* disable escape clock. */ - exynos_mipi_dsi_enable_byte_clock(dsim, 0); - - if (byte_clk_sel == DSIM_PLL_OUT_DIV8) - exynos_mipi_dsi_pll_on(dsim, 0); - } - - return 0; -} - -int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim) -{ - dsim->state = DSIM_STATE_INIT; - - switch (dsim->dsim_config->e_no_data_lane) { - case DSIM_DATA_LANE_1: - dsim->data_lane = DSIM_LANE_DATA0; - break; - case DSIM_DATA_LANE_2: - dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1; - break; - case DSIM_DATA_LANE_3: - dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 | - DSIM_LANE_DATA2; - break; - case DSIM_DATA_LANE_4: - dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 | - DSIM_LANE_DATA2 | DSIM_LANE_DATA3; - break; - default: - dev_info(dsim->dev, "data lane is invalid.\n"); - return -EINVAL; - } - - exynos_mipi_dsi_sw_reset(dsim); - exynos_mipi_dsi_func_reset(dsim); - - exynos_mipi_dsi_dp_dn_swap(dsim, 0); - - return 0; -} - -void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim) -{ - unsigned int src = 0; - - src = (INTSRC_SFR_FIFO_EMPTY | INTSRC_RX_DATA_DONE); - exynos_mipi_dsi_set_interrupt(dsim, src, 1); - - src = 0; - src = ~(INTMSK_RX_DONE | INTMSK_FIFO_EMPTY); - exynos_mipi_dsi_set_interrupt_mask(dsim, src, 1); -} - -int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim, - unsigned int enable) -{ - /* enable only frame done interrupt */ - exynos_mipi_dsi_set_interrupt_mask(dsim, INTMSK_FRAME_DONE, enable); - - return 0; -} - -void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim, - unsigned int enable) -{ - - /* consider Main display and Sub display. */ - - exynos_mipi_dsi_set_main_stand_by(dsim, enable); -} - -int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, - struct mipi_dsim_config *dsim_config) -{ - struct mipi_dsim_platform_data *dsim_pd; - struct fb_videomode *timing; - - dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd; - timing = (struct fb_videomode *)dsim_pd->lcd_panel_info; - - /* in case of VIDEO MODE (RGB INTERFACE), it sets polarities. */ - if (dsim_config->e_interface == (u32) DSIM_VIDEO) { - if (dsim_config->auto_vertical_cnt == 0) { - exynos_mipi_dsi_set_main_disp_vporch(dsim, - dsim_config->cmd_allow, - timing->lower_margin, - timing->upper_margin); - exynos_mipi_dsi_set_main_disp_hporch(dsim, - timing->right_margin, - timing->left_margin); - exynos_mipi_dsi_set_main_disp_sync_area(dsim, - timing->vsync_len, - timing->hsync_len); - } - } - - exynos_mipi_dsi_set_main_disp_resol(dsim, timing->xres, - timing->yres); - - exynos_mipi_dsi_display_config(dsim, dsim_config); - - dev_info(dsim->dev, "lcd panel ==> width = %d, height = %d\n", - timing->xres, timing->yres); - - return 0; -} - -int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim) -{ - unsigned int time_out = 100; - - switch (dsim->state) { - case DSIM_STATE_INIT: - exynos_mipi_dsi_init_fifo_pointer(dsim, 0x1f); - - /* dsi configuration */ - exynos_mipi_dsi_init_config(dsim); - exynos_mipi_dsi_enable_lane(dsim, DSIM_LANE_CLOCK, 1); - exynos_mipi_dsi_enable_lane(dsim, dsim->data_lane, 1); - - /* set clock configuration */ - exynos_mipi_dsi_set_clock(dsim, dsim->dsim_config->e_byte_clk, 1); - - /* check clock and data lane state are stop state */ - while (!(exynos_mipi_dsi_is_lane_state(dsim))) { - time_out--; - if (time_out == 0) { - dev_err(dsim->dev, - "DSI Master is not stop state.\n"); - dev_err(dsim->dev, - "Check initialization process\n"); - - return -EINVAL; - } - } - if (time_out != 0) { - dev_info(dsim->dev, - "DSI Master driver has been completed.\n"); - dev_info(dsim->dev, "DSI Master state is stop state\n"); - } - - dsim->state = DSIM_STATE_STOP; - - /* BTA sequence counters */ - exynos_mipi_dsi_set_stop_state_counter(dsim, - dsim->dsim_config->stop_holding_cnt); - exynos_mipi_dsi_set_bta_timeout(dsim, - dsim->dsim_config->bta_timeout); - exynos_mipi_dsi_set_lpdr_timeout(dsim, - dsim->dsim_config->rx_timeout); - - return 0; - default: - dev_info(dsim->dev, "DSI Master is already init.\n"); - return 0; - } - - return 0; -} - -int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim) -{ - if (dsim->state != DSIM_STATE_STOP) { - dev_warn(dsim->dev, "DSIM is not in stop state.\n"); - return 0; - } - - if (dsim->e_clk_src == DSIM_EXT_CLK_BYPASS) { - dev_warn(dsim->dev, "clock source is external bypass.\n"); - return 0; - } - - dsim->state = DSIM_STATE_HSCLKEN; - - /* set LCDC and CPU transfer mode to HS. */ - exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0); - exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0); - exynos_mipi_dsi_enable_hs_clock(dsim, 1); - - return 0; -} - -int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim, - unsigned int mode) -{ - if (mode) { - if (dsim->state != DSIM_STATE_HSCLKEN) { - dev_err(dsim->dev, "HS Clock lane is not enabled.\n"); - return -EINVAL; - } - - exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0); - } else { - if (dsim->state == DSIM_STATE_INIT || dsim->state == - DSIM_STATE_ULPS) { - dev_err(dsim->dev, - "DSI Master is not STOP or HSDT state.\n"); - return -EINVAL; - } - - exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0); - } - - return 0; -} - -int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim) -{ - return _exynos_mipi_dsi_get_frame_done_status(dsim); -} - -int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim) -{ - _exynos_mipi_dsi_clear_frame_done(dsim); - - return 0; -} - -int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim, - unsigned int val) -{ - int try = TRY_FIFO_CLEAR; - - exynos_mipi_dsi_sw_reset_release(dsim); - exynos_mipi_dsi_func_reset(dsim); - - do { - if (exynos_mipi_dsi_get_sw_reset_release(dsim)) { - exynos_mipi_dsi_init_interrupt(dsim); - dev_dbg(dsim->dev, "reset release done.\n"); - return 0; - } - } while (--try); - - dev_err(dsim->dev, "failed to clear dsim fifo.\n"); - return -EAGAIN; -} - -MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>"); -MODULE_DESCRIPTION("Samsung SoC MIPI-DSI common driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.h b/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.h deleted file mode 100644 index 412552274df3..000000000000 --- a/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.h +++ /dev/null @@ -1,46 +0,0 @@ -/* linux/drivers/video/exynos_mipi_dsi_common.h - * - * Header file for Samsung SoC MIPI-DSI common driver. - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd - * - * InKi Dae <inki.dae@samsung.com> - * Donghwa Lee <dh09.lee@samsung.com> - * - * 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 _EXYNOS_MIPI_DSI_COMMON_H -#define _EXYNOS_MIPI_DSI_COMMON_H - -static DECLARE_COMPLETION(dsim_rd_comp); -static DECLARE_COMPLETION(dsim_wr_comp); - -int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id, - const unsigned char *data0, unsigned int data_size); -int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id, - unsigned int data0, unsigned int req_size, u8 *rx_buf); -irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id); -void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim); -int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim); -void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim, - unsigned int enable); -int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim, - struct mipi_dsim_config *dsim_info); -int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim); -int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim); -int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim, - unsigned int mode); -int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim, - unsigned int enable); -int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim); -int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim); - -extern struct fb_info *registered_fb[FB_MAX] __read_mostly; - -int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim, - unsigned int val); - -#endif /* _EXYNOS_MIPI_DSI_COMMON_H */ diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.c b/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.c deleted file mode 100644 index c148d06540c1..000000000000 --- a/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.c +++ /dev/null @@ -1,618 +0,0 @@ -/* linux/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c - * - * Samsung SoC MIPI-DSI lowlevel driver. - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd - * - * InKi Dae, <inki.dae@samsung.com> - * Donghwa Lee, <dh09.lee@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/mutex.h> -#include <linux/wait.h> -#include <linux/delay.h> -#include <linux/fs.h> -#include <linux/mm.h> -#include <linux/ctype.h> -#include <linux/platform_device.h> -#include <linux/io.h> - -#include <video/exynos_mipi_dsim.h> - -#include "exynos_mipi_dsi_regs.h" -#include "exynos_mipi_dsi_lowlevel.h" - -void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim) -{ - unsigned int reg; - - reg = readl(dsim->reg_base + EXYNOS_DSIM_SWRST); - - reg |= DSIM_FUNCRST; - - writel(reg, dsim->reg_base + EXYNOS_DSIM_SWRST); -} - -void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim) -{ - unsigned int reg; - - reg = readl(dsim->reg_base + EXYNOS_DSIM_SWRST); - - reg |= DSIM_SWRST; - - writel(reg, dsim->reg_base + EXYNOS_DSIM_SWRST); -} - -void exynos_mipi_dsi_sw_reset_release(struct mipi_dsim_device *dsim) -{ - unsigned int reg; - - reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); - - reg |= INTSRC_SW_RST_RELEASE; - - writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC); -} - -int exynos_mipi_dsi_get_sw_reset_release(struct mipi_dsim_device *dsim) -{ - return (readl(dsim->reg_base + EXYNOS_DSIM_INTSRC)) & - INTSRC_SW_RST_RELEASE; -} - -unsigned int exynos_mipi_dsi_read_interrupt_mask(struct mipi_dsim_device *dsim) -{ - unsigned int reg; - - reg = readl(dsim->reg_base + EXYNOS_DSIM_INTMSK); - - return reg; -} - -void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim, - unsigned int mode, unsigned int mask) -{ - unsigned int reg = 0; - - if (mask) - reg |= mode; - else - reg &= ~mode; - - writel(reg, dsim->reg_base + EXYNOS_DSIM_INTMSK); -} - -void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim, - unsigned int cfg) -{ - unsigned int reg; - - reg = readl(dsim->reg_base + EXYNOS_DSIM_FIFOCTRL); - - writel(reg & ~(cfg), dsim->reg_base + EXYNOS_DSIM_FIFOCTRL); - mdelay(10); - reg |= cfg; - - writel(reg, dsim->reg_base + EXYNOS_DSIM_FIFOCTRL); -} - -/* - * this function set PLL P, M and S value in D-PHY - */ -void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, - unsigned int value) -{ - writel(DSIM_AFC_CTL(value), dsim->reg_base + EXYNOS_DSIM_PHYACCHR); -} - -void exynos_mipi_dsi_set_main_stand_by(struct mipi_dsim_device *dsim, - unsigned int enable) -{ - unsigned int reg; - - reg = readl(dsim->reg_base + EXYNOS_DSIM_MDRESOL); - - reg &= ~DSIM_MAIN_STAND_BY; - - if (enable) - reg |= DSIM_MAIN_STAND_BY; - - writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL); -} - -void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim, - unsigned int width_resol, unsigned int height_resol) -{ - unsigned int reg; - - /* standby should be set after configuration so set to not ready*/ - reg = (readl(dsim->reg_base + EXYNOS_DSIM_MDRESOL)) & - ~(DSIM_MAIN_STAND_BY); - writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL); - - reg &= ~((0x7ff << 16) | (0x7ff << 0)); - reg |= DSIM_MAIN_VRESOL(height_resol) | DSIM_MAIN_HRESOL(width_resol); - - reg |= DSIM_MAIN_STAND_BY; - writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL); -} - -void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim, - unsigned int cmd_allow, unsigned int vfront, unsigned int vback) -{ - unsigned int reg; - - reg = (readl(dsim->reg_base + EXYNOS_DSIM_MVPORCH)) & - ~((DSIM_CMD_ALLOW_MASK) | (DSIM_STABLE_VFP_MASK) | - (DSIM_MAIN_VBP_MASK)); - - reg |= (DSIM_CMD_ALLOW_SHIFT(cmd_allow & 0xf) | - DSIM_STABLE_VFP_SHIFT(vfront & 0x7ff) | - DSIM_MAIN_VBP_SHIFT(vback & 0x7ff)); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_MVPORCH); -} - -void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim, - unsigned int front, unsigned int back) -{ - unsigned int reg; - - reg = (readl(dsim->reg_base + EXYNOS_DSIM_MHPORCH)) & - ~((DSIM_MAIN_HFP_MASK) | (DSIM_MAIN_HBP_MASK)); - - reg |= DSIM_MAIN_HFP_SHIFT(front) | DSIM_MAIN_HBP_SHIFT(back); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_MHPORCH); -} - -void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim, - unsigned int vert, unsigned int hori) -{ - unsigned int reg; - - reg = (readl(dsim->reg_base + EXYNOS_DSIM_MSYNC)) & - ~((DSIM_MAIN_VSA_MASK) | (DSIM_MAIN_HSA_MASK)); - - reg |= (DSIM_MAIN_VSA_SHIFT(vert & 0x3ff) | - DSIM_MAIN_HSA_SHIFT(hori)); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_MSYNC); -} - -void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim, - unsigned int vert, unsigned int hori) -{ - unsigned int reg; - - reg = (readl(dsim->reg_base + EXYNOS_DSIM_SDRESOL)) & - ~(DSIM_SUB_STANDY_MASK); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL); - - reg &= ~(DSIM_SUB_VRESOL_MASK) | ~(DSIM_SUB_HRESOL_MASK); - reg |= (DSIM_SUB_VRESOL_SHIFT(vert & 0x7ff) | - DSIM_SUB_HRESOL_SHIFT(hori & 0x7ff)); - writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL); - - reg |= DSIM_SUB_STANDY_SHIFT(1); - writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL); -} - -void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim) -{ - struct mipi_dsim_config *dsim_config = dsim->dsim_config; - - unsigned int cfg = (readl(dsim->reg_base + EXYNOS_DSIM_CONFIG)) & - ~((1 << 28) | (0x1f << 20) | (0x3 << 5)); - - cfg = ((DSIM_AUTO_FLUSH(dsim_config->auto_flush)) | - (DSIM_EOT_DISABLE(dsim_config->eot_disable)) | - (DSIM_AUTO_MODE_SHIFT(dsim_config->auto_vertical_cnt)) | - (DSIM_HSE_MODE_SHIFT(dsim_config->hse)) | - (DSIM_HFP_MODE_SHIFT(dsim_config->hfp)) | - (DSIM_HBP_MODE_SHIFT(dsim_config->hbp)) | - (DSIM_HSA_MODE_SHIFT(dsim_config->hsa)) | - (DSIM_NUM_OF_DATALANE_SHIFT(dsim_config->e_no_data_lane))); - - writel(cfg, dsim->reg_base + EXYNOS_DSIM_CONFIG); -} - -void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim, - struct mipi_dsim_config *dsim_config) -{ - u32 reg = (readl(dsim->reg_base + EXYNOS_DSIM_CONFIG)) & - ~((0x3 << 26) | (1 << 25) | (0x3 << 18) | (0x7 << 12) | - (0x3 << 16) | (0x7 << 8)); - - if (dsim_config->e_interface == DSIM_VIDEO) - reg |= (1 << 25); - else if (dsim_config->e_interface == DSIM_COMMAND) - reg &= ~(1 << 25); - else { - dev_err(dsim->dev, "unknown lcd type.\n"); - return; - } - - /* main lcd */ - reg |= ((u8) (dsim_config->e_burst_mode) & 0x3) << 26 | - ((u8) (dsim_config->e_virtual_ch) & 0x3) << 18 | - ((u8) (dsim_config->e_pixel_format) & 0x7) << 12; - - writel(reg, dsim->reg_base + EXYNOS_DSIM_CONFIG); -} - -void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, unsigned int lane, - unsigned int enable) -{ - unsigned int reg; - - reg = readl(dsim->reg_base + EXYNOS_DSIM_CONFIG); - - if (enable) - reg |= DSIM_LANE_ENx(lane); - else - reg &= ~DSIM_LANE_ENx(lane); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_CONFIG); -} - - -void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, - unsigned int count) -{ - unsigned int cfg; - - /* get the data lane number. */ - cfg = DSIM_NUM_OF_DATALANE_SHIFT(count); - - writel(cfg, dsim->reg_base + EXYNOS_DSIM_CONFIG); -} - -void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, unsigned int enable, - unsigned int afc_code) -{ - unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PHYACCHR); - - if (enable) { - reg |= (1 << 14); - reg &= ~(0x7 << 5); - reg |= (afc_code & 0x7) << 5; - } else - reg &= ~(1 << 14); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_PHYACCHR); -} - -void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim, - unsigned int enable) -{ - unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & - ~(DSIM_PLL_BYPASS_SHIFT(0x1)); - - reg |= DSIM_PLL_BYPASS_SHIFT(enable); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); -} - -void exynos_mipi_dsi_set_pll_pms(struct mipi_dsim_device *dsim, unsigned int p, - unsigned int m, unsigned int s) -{ - unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL); - - reg |= ((p & 0x3f) << 13) | ((m & 0x1ff) << 4) | ((s & 0x7) << 1); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); -} - -void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim, - unsigned int freq_band) -{ - unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & - ~(DSIM_FREQ_BAND_SHIFT(0x1f)); - - reg |= DSIM_FREQ_BAND_SHIFT(freq_band & 0x1f); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); -} - -void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim, - unsigned int pre_divider, unsigned int main_divider, - unsigned int scaler) -{ - unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & - ~(0x7ffff << 1); - - reg |= (pre_divider & 0x3f) << 13 | (main_divider & 0x1ff) << 4 | - (scaler & 0x7) << 1; - - writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); -} - -void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim, - unsigned int lock_time) -{ - writel(lock_time, dsim->reg_base + EXYNOS_DSIM_PLLTMR); -} - -void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, unsigned int enable) -{ - unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & - ~(DSIM_PLL_EN_SHIFT(0x1)); - - reg |= DSIM_PLL_EN_SHIFT(enable & 0x1); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); -} - -void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim, - unsigned int src) -{ - unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & - ~(DSIM_BYTE_CLK_SRC_SHIFT(0x3)); - - reg |= (DSIM_BYTE_CLK_SRC_SHIFT(src)); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); -} - -void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim, - unsigned int enable) -{ - unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & - ~(DSIM_BYTE_CLKEN_SHIFT(0x1)); - - reg |= DSIM_BYTE_CLKEN_SHIFT(enable); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); -} - -void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim, - unsigned int enable, unsigned int prs_val) -{ - unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & - ~(DSIM_ESC_CLKEN_SHIFT(0x1) | 0xffff); - - reg |= DSIM_ESC_CLKEN_SHIFT(enable); - if (enable) - reg |= prs_val; - - writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); -} - -void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim, - unsigned int lane_sel, unsigned int enable) -{ - unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL); - - if (enable) - reg |= DSIM_LANE_ESC_CLKEN(lane_sel); - else - - reg &= ~DSIM_LANE_ESC_CLKEN(lane_sel); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); -} - -void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim, - unsigned int enable) -{ - unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE)) & - ~(DSIM_FORCE_STOP_STATE_SHIFT(0x1)); - - reg |= (DSIM_FORCE_STOP_STATE_SHIFT(enable & 0x1)); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); -} - -unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim) -{ - unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_STATUS); - - /** - * check clock and data lane states. - * if MIPI-DSI controller was enabled at bootloader then - * TX_READY_HS_CLK is enabled otherwise STOP_STATE_CLK. - * so it should be checked for two case. - */ - if ((reg & DSIM_STOP_STATE_DAT(0xf)) && - ((reg & DSIM_STOP_STATE_CLK) || - (reg & DSIM_TX_READY_HS_CLK))) - return 1; - - return 0; -} - -void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim, - unsigned int cnt_val) -{ - unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE)) & - ~(DSIM_STOP_STATE_CNT_SHIFT(0x7ff)); - - reg |= (DSIM_STOP_STATE_CNT_SHIFT(cnt_val & 0x7ff)); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); -} - -void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim, - unsigned int timeout) -{ - unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_TIMEOUT)) & - ~(DSIM_BTA_TOUT_SHIFT(0xff)); - - reg |= (DSIM_BTA_TOUT_SHIFT(timeout)); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_TIMEOUT); -} - -void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim, - unsigned int timeout) -{ - unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_TIMEOUT)) & - ~(DSIM_LPDR_TOUT_SHIFT(0xffff)); - - reg |= (DSIM_LPDR_TOUT_SHIFT(timeout)); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_TIMEOUT); -} - -void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim, - unsigned int lp) -{ - unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE); - - reg &= ~DSIM_CMD_LPDT_LP; - - if (lp) - reg |= DSIM_CMD_LPDT_LP; - - writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); -} - -void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim, - unsigned int lp) -{ - unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE); - - reg &= ~DSIM_TX_LPDT_LP; - - if (lp) - reg |= DSIM_TX_LPDT_LP; - - writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE); -} - -void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim, - unsigned int enable) -{ - unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) & - ~(DSIM_TX_REQUEST_HSCLK_SHIFT(0x1)); - - reg |= DSIM_TX_REQUEST_HSCLK_SHIFT(enable); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL); -} - -void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim, - unsigned int swap_en) -{ - unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PHYACCHR1); - - reg &= ~(0x3 << 0); - reg |= (swap_en & 0x3) << 0; - - writel(reg, dsim->reg_base + EXYNOS_DSIM_PHYACCHR1); -} - -void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim, - unsigned int hs_zero) -{ - unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & - ~(0xf << 28); - - reg |= ((hs_zero & 0xf) << 28); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); -} - -void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep) -{ - unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) & - ~(0x7 << 20); - - reg |= ((prep & 0x7) << 20); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL); -} - -unsigned int exynos_mipi_dsi_read_interrupt(struct mipi_dsim_device *dsim) -{ - return readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); -} - -void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim, - unsigned int src) -{ - unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); - - reg |= src; - - writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC); -} - -void exynos_mipi_dsi_set_interrupt(struct mipi_dsim_device *dsim, - unsigned int src, unsigned int enable) -{ - unsigned int reg = 0; - - if (enable) - reg |= src; - else - reg &= ~src; - - writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC); -} - -unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim) -{ - unsigned int reg; - - reg = readl(dsim->reg_base + EXYNOS_DSIM_STATUS); - - return reg & (1 << 31) ? 1 : 0; -} - -unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim) -{ - return readl(dsim->reg_base + EXYNOS_DSIM_FIFOCTRL) & ~(0x1f); -} - -void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, - unsigned int di, unsigned int data0, unsigned int data1) -{ - unsigned int reg = (data1 << 16) | (data0 << 8) | ((di & 0x3f) << 0); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_PKTHDR); -} - -void exynos_mipi_dsi_rd_tx_header(struct mipi_dsim_device *dsim, - unsigned int di, unsigned int data0) -{ - unsigned int reg = (data0 << 8) | (di << 0); - - writel(reg, dsim->reg_base + EXYNOS_DSIM_PKTHDR); -} - -unsigned int exynos_mipi_dsi_rd_rx_fifo(struct mipi_dsim_device *dsim) -{ - return readl(dsim->reg_base + EXYNOS_DSIM_RXFIFO); -} - -unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim) -{ - unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); - - return (reg & INTSRC_FRAME_DONE) ? 1 : 0; -} - -void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim) -{ - unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC); - - writel(reg | INTSRC_FRAME_DONE, dsim->reg_base + - EXYNOS_DSIM_INTSRC); -} - -void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim, - unsigned int tx_data) -{ - writel(tx_data, dsim->reg_base + EXYNOS_DSIM_PAYLOAD); -} diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.h b/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.h deleted file mode 100644 index 85460701c7ea..000000000000 --- a/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.h +++ /dev/null @@ -1,112 +0,0 @@ -/* linux/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h - * - * Header file for Samsung SoC MIPI-DSI lowlevel driver. - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd - * - * InKi Dae <inki.dae@samsung.com> - * Donghwa Lee <dh09.lee@samsung.com> - * - * 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 _EXYNOS_MIPI_DSI_LOWLEVEL_H -#define _EXYNOS_MIPI_DSI_LOWLEVEL_H - -void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim); -void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim); -void exynos_mipi_dsi_sw_reset_release(struct mipi_dsim_device *dsim); -int exynos_mipi_dsi_get_sw_reset_release(struct mipi_dsim_device *dsim); -void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim, - unsigned int mode, unsigned int mask); -void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, - unsigned int count); -void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim, - unsigned int cfg); -void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, - unsigned int value); -void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim, - unsigned int value); -void exynos_mipi_dsi_set_main_stand_by(struct mipi_dsim_device *dsim, - unsigned int enable); -void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim, - unsigned int width_resol, unsigned int height_resol); -void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim, - unsigned int cmd_allow, unsigned int vfront, unsigned int vback); -void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim, - unsigned int front, unsigned int back); -void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim, - unsigned int vert, unsigned int hori); -void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim, - unsigned int vert, unsigned int hori); -void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim); -void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim, - struct mipi_dsim_config *dsim_config); -void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim, - unsigned int count); -void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, unsigned int lane, - unsigned int enable); -void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, unsigned int enable, - unsigned int afc_code); -void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim, - unsigned int enable); -void exynos_mipi_dsi_set_pll_pms(struct mipi_dsim_device *dsim, unsigned int p, - unsigned int m, unsigned int s); -void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim, - unsigned int freq_band); -void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim, - unsigned int pre_divider, unsigned int main_divider, - unsigned int scaler); -void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim, - unsigned int lock_time); -void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, - unsigned int enable); -void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim, - unsigned int src); -void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim, - unsigned int enable); -void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim, - unsigned int enable, unsigned int prs_val); -void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim, - unsigned int lane_sel, unsigned int enable); -void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim, - unsigned int enable); -unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim); -void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim, - unsigned int cnt_val); -void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim, - unsigned int timeout); -void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim, - unsigned int timeout); -void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim, - unsigned int lp); -void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim, - unsigned int lp); -void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim, - unsigned int enable); -void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim, - unsigned int swap_en); -void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim, - unsigned int hs_zero); -void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep); -unsigned int exynos_mipi_dsi_read_interrupt(struct mipi_dsim_device *dsim); -unsigned int exynos_mipi_dsi_read_interrupt_mask(struct mipi_dsim_device *dsim); -void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim, - unsigned int src); -void exynos_mipi_dsi_set_interrupt(struct mipi_dsim_device *dsim, - unsigned int src, unsigned int enable); -unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim); -unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim); -unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim); -void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim); -void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, unsigned int di, - unsigned int data0, unsigned int data1); -void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim, - unsigned int tx_data); -void exynos_mipi_dsi_rd_tx_header(struct mipi_dsim_device *dsim, - unsigned int data0, unsigned int data1); -unsigned int exynos_mipi_dsi_rd_rx_fifo(struct mipi_dsim_device *dsim); - -#endif /* _EXYNOS_MIPI_DSI_LOWLEVEL_H */ diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_regs.h b/drivers/video/fbdev/exynos/exynos_mipi_dsi_regs.h deleted file mode 100644 index 4227106d3fd0..000000000000 --- a/drivers/video/fbdev/exynos/exynos_mipi_dsi_regs.h +++ /dev/null @@ -1,149 +0,0 @@ -/* linux/driver/video/exynos/exynos_mipi_dsi_regs.h - * - * Register definition file for Samsung MIPI-DSIM driver - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd - * - * InKi Dae <inki.dae@samsung.com> - * Donghwa Lee <dh09.lee@samsung.com> - * - * 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 _EXYNOS_MIPI_DSI_REGS_H -#define _EXYNOS_MIPI_DSI_REGS_H - -#define EXYNOS_DSIM_STATUS 0x0 /* Status register */ -#define EXYNOS_DSIM_SWRST 0x4 /* Software reset register */ -#define EXYNOS_DSIM_CLKCTRL 0x8 /* Clock control register */ -#define EXYNOS_DSIM_TIMEOUT 0xc /* Time out register */ -#define EXYNOS_DSIM_CONFIG 0x10 /* Configuration register */ -#define EXYNOS_DSIM_ESCMODE 0x14 /* Escape mode register */ - -/* Main display image resolution register */ -#define EXYNOS_DSIM_MDRESOL 0x18 -#define EXYNOS_DSIM_MVPORCH 0x1c /* Main display Vporch register */ -#define EXYNOS_DSIM_MHPORCH 0x20 /* Main display Hporch register */ -#define EXYNOS_DSIM_MSYNC 0x24 /* Main display sync area register */ - -/* Sub display image resolution register */ -#define EXYNOS_DSIM_SDRESOL 0x28 -#define EXYNOS_DSIM_INTSRC 0x2c /* Interrupt source register */ -#define EXYNOS_DSIM_INTMSK 0x30 /* Interrupt mask register */ -#define EXYNOS_DSIM_PKTHDR 0x34 /* Packet Header FIFO register */ -#define EXYNOS_DSIM_PAYLOAD 0x38 /* Payload FIFO register */ -#define EXYNOS_DSIM_RXFIFO 0x3c /* Read FIFO register */ -#define EXYNOS_DSIM_FIFOTHLD 0x40 /* FIFO threshold level register */ -#define EXYNOS_DSIM_FIFOCTRL 0x44 /* FIFO status and control register */ - -/* FIFO memory AC characteristic register */ -#define EXYNOS_DSIM_PLLCTRL 0x4c /* PLL control register */ -#define EXYNOS_DSIM_PLLTMR 0x50 /* PLL timer register */ -#define EXYNOS_DSIM_PHYACCHR 0x54 /* D-PHY AC characteristic register */ -#define EXYNOS_DSIM_PHYACCHR1 0x58 /* D-PHY AC characteristic register1 */ - -/* DSIM_STATUS */ -#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0) -#define DSIM_STOP_STATE_CLK (1 << 8) -#define DSIM_TX_READY_HS_CLK (1 << 10) - -/* DSIM_SWRST */ -#define DSIM_FUNCRST (1 << 16) -#define DSIM_SWRST (1 << 0) - -/* EXYNOS_DSIM_TIMEOUT */ -#define DSIM_LPDR_TOUT_SHIFT(x) ((x) << 0) -#define DSIM_BTA_TOUT_SHIFT(x) ((x) << 16) - -/* EXYNOS_DSIM_CLKCTRL */ -#define DSIM_LANE_ESC_CLKEN(x) (((x) & 0x1f) << 19) -#define DSIM_BYTE_CLKEN_SHIFT(x) ((x) << 24) -#define DSIM_BYTE_CLK_SRC_SHIFT(x) ((x) << 25) -#define DSIM_PLL_BYPASS_SHIFT(x) ((x) << 27) -#define DSIM_ESC_CLKEN_SHIFT(x) ((x) << 28) -#define DSIM_TX_REQUEST_HSCLK_SHIFT(x) ((x) << 31) - -/* EXYNOS_DSIM_CONFIG */ -#define DSIM_LANE_ENx(x) (((x) & 0x1f) << 0) -#define DSIM_NUM_OF_DATALANE_SHIFT(x) ((x) << 5) -#define DSIM_HSA_MODE_SHIFT(x) ((x) << 20) -#define DSIM_HBP_MODE_SHIFT(x) ((x) << 21) -#define DSIM_HFP_MODE_SHIFT(x) ((x) << 22) -#define DSIM_HSE_MODE_SHIFT(x) ((x) << 23) -#define DSIM_AUTO_MODE_SHIFT(x) ((x) << 24) -#define DSIM_EOT_DISABLE(x) ((x) << 28) -#define DSIM_AUTO_FLUSH(x) ((x) << 29) - -#define DSIM_NUM_OF_DATA_LANE(x) ((x) << DSIM_NUM_OF_DATALANE_SHIFT) - -/* EXYNOS_DSIM_ESCMODE */ -#define DSIM_TX_LPDT_LP (1 << 6) -#define DSIM_CMD_LPDT_LP (1 << 7) -#define DSIM_FORCE_STOP_STATE_SHIFT(x) ((x) << 20) -#define DSIM_STOP_STATE_CNT_SHIFT(x) ((x) << 21) - -/* EXYNOS_DSIM_MDRESOL */ -#define DSIM_MAIN_STAND_BY (1 << 31) -#define DSIM_MAIN_VRESOL(x) (((x) & 0x7ff) << 16) -#define DSIM_MAIN_HRESOL(x) (((x) & 0X7ff) << 0) - -/* EXYNOS_DSIM_MVPORCH */ -#define DSIM_CMD_ALLOW_SHIFT(x) ((x) << 28) -#define DSIM_STABLE_VFP_SHIFT(x) ((x) << 16) -#define DSIM_MAIN_VBP_SHIFT(x) ((x) << 0) -#define DSIM_CMD_ALLOW_MASK (0xf << 28) -#define DSIM_STABLE_VFP_MASK (0x7ff << 16) -#define DSIM_MAIN_VBP_MASK (0x7ff << 0) - -/* EXYNOS_DSIM_MHPORCH */ -#define DSIM_MAIN_HFP_SHIFT(x) ((x) << 16) -#define DSIM_MAIN_HBP_SHIFT(x) ((x) << 0) -#define DSIM_MAIN_HFP_MASK ((0xffff) << 16) -#define DSIM_MAIN_HBP_MASK ((0xffff) << 0) - -/* EXYNOS_DSIM_MSYNC */ -#define DSIM_MAIN_VSA_SHIFT(x) ((x) << 22) -#define DSIM_MAIN_HSA_SHIFT(x) ((x) << 0) -#define DSIM_MAIN_VSA_MASK ((0x3ff) << 22) -#define DSIM_MAIN_HSA_MASK ((0xffff) << 0) - -/* EXYNOS_DSIM_SDRESOL */ -#define DSIM_SUB_STANDY_SHIFT(x) ((x) << 31) -#define DSIM_SUB_VRESOL_SHIFT(x) ((x) << 16) -#define DSIM_SUB_HRESOL_SHIFT(x) ((x) << 0) -#define DSIM_SUB_STANDY_MASK ((0x1) << 31) -#define DSIM_SUB_VRESOL_MASK ((0x7ff) << 16) -#define DSIM_SUB_HRESOL_MASK ((0x7ff) << 0) - -/* EXYNOS_DSIM_INTSRC */ -#define INTSRC_PLL_STABLE (1 << 31) -#define INTSRC_SW_RST_RELEASE (1 << 30) -#define INTSRC_SFR_FIFO_EMPTY (1 << 29) -#define INTSRC_FRAME_DONE (1 << 24) -#define INTSRC_RX_DATA_DONE (1 << 18) - -/* EXYNOS_DSIM_INTMSK */ -#define INTMSK_FIFO_EMPTY (1 << 29) -#define INTMSK_BTA (1 << 25) -#define INTMSK_FRAME_DONE (1 << 24) -#define INTMSK_RX_TIMEOUT (1 << 21) -#define INTMSK_BTA_TIMEOUT (1 << 20) -#define INTMSK_RX_DONE (1 << 18) -#define INTMSK_RX_TE (1 << 17) -#define INTMSK_RX_ACK (1 << 16) -#define INTMSK_RX_ECC_ERR (1 << 15) -#define INTMSK_RX_CRC_ERR (1 << 14) - -/* EXYNOS_DSIM_FIFOCTRL */ -#define SFR_HEADER_EMPTY (1 << 22) - -/* EXYNOS_DSIM_PHYACCHR */ -#define DSIM_AFC_CTL(x) (((x) & 0x7) << 5) - -/* EXYNOS_DSIM_PLLCTRL */ -#define DSIM_PLL_EN_SHIFT(x) ((x) << 23) -#define DSIM_FREQ_BAND_SHIFT(x) ((x) << 24) - -#endif /* _EXYNOS_MIPI_DSI_REGS_H */ diff --git a/drivers/video/fbdev/exynos/s6e8ax0.c b/drivers/video/fbdev/exynos/s6e8ax0.c deleted file mode 100644 index de2f3e793786..000000000000 --- a/drivers/video/fbdev/exynos/s6e8ax0.c +++ /dev/null @@ -1,887 +0,0 @@ -/* linux/drivers/video/exynos/s6e8ax0.c - * - * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver. - * - * Inki Dae, <inki.dae@samsung.com> - * Donghwa Lee, <dh09.lee@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/mutex.h> -#include <linux/wait.h> -#include <linux/ctype.h> -#include <linux/io.h> -#include <linux/delay.h> -#include <linux/irq.h> -#include <linux/interrupt.h> -#include <linux/lcd.h> -#include <linux/fb.h> -#include <linux/backlight.h> -#include <linux/regulator/consumer.h> - -#include <video/mipi_display.h> -#include <video/exynos_mipi_dsim.h> - -#define LDI_MTP_LENGTH 24 -#define DSIM_PM_STABLE_TIME 10 -#define MIN_BRIGHTNESS 0 -#define MAX_BRIGHTNESS 24 -#define GAMMA_TABLE_COUNT 26 - -#define POWER_IS_ON(pwr) ((pwr) == FB_BLANK_UNBLANK) -#define POWER_IS_OFF(pwr) ((pwr) == FB_BLANK_POWERDOWN) -#define POWER_IS_NRM(pwr) ((pwr) == FB_BLANK_NORMAL) - -#define lcd_to_master(a) (a->dsim_dev->master) -#define lcd_to_master_ops(a) ((lcd_to_master(a))->master_ops) - -enum { - DSIM_NONE_STATE = 0, - DSIM_RESUME_COMPLETE = 1, - DSIM_FRAME_DONE = 2, -}; - -struct s6e8ax0 { - struct device *dev; - unsigned int power; - unsigned int id; - unsigned int gamma; - unsigned int acl_enable; - unsigned int cur_acl; - - struct lcd_device *ld; - struct backlight_device *bd; - - struct mipi_dsim_lcd_device *dsim_dev; - struct lcd_platform_data *ddi_pd; - struct mutex lock; - bool enabled; -}; - - -static struct regulator_bulk_data supplies[] = { - { .supply = "vdd3", }, - { .supply = "vci", }, -}; - -static void s6e8ax0_regulator_enable(struct s6e8ax0 *lcd) -{ - int ret = 0; - struct lcd_platform_data *pd = NULL; - - pd = lcd->ddi_pd; - mutex_lock(&lcd->lock); - if (!lcd->enabled) { - ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); - if (ret) - goto out; - - lcd->enabled = true; - } - msleep(pd->power_on_delay); -out: - mutex_unlock(&lcd->lock); -} - -static void s6e8ax0_regulator_disable(struct s6e8ax0 *lcd) -{ - int ret = 0; - - mutex_lock(&lcd->lock); - if (lcd->enabled) { - ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); - if (ret) - goto out; - - lcd->enabled = false; - } -out: - mutex_unlock(&lcd->lock); -} - -static const unsigned char s6e8ax0_22_gamma_30[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf, - 0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0, - 0x00, 0x61, 0x00, 0x5a, 0x00, 0x74, -}; - -static const unsigned char s6e8ax0_22_gamma_50[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0, - 0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb, - 0x00, 0x70, 0x00, 0x68, 0x00, 0x86, -}; - -static const unsigned char s6e8ax0_22_gamma_60[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4, - 0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba, - 0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d, -}; - -static const unsigned char s6e8ax0_22_gamma_70[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8, - 0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9, - 0x00, 0x7a, 0x00, 0x72, 0x00, 0x93, -}; - -static const unsigned char s6e8ax0_22_gamma_80[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9, - 0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb, - 0x00, 0x7f, 0x00, 0x77, 0x00, 0x99, -}; - -static const unsigned char s6e8ax0_22_gamma_90[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc, - 0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9, - 0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e, -}; - -static const unsigned char s6e8ax0_22_gamma_100[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce, - 0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6, - 0x00, 0x88, 0x00, 0x80, 0x00, 0xa5, -}; - -static const unsigned char s6e8ax0_22_gamma_120[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf, - 0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6, - 0x00, 0x8f, 0x00, 0x86, 0x00, 0xac, -}; - -static const unsigned char s6e8ax0_22_gamma_130[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0, - 0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4, - 0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1, -}; - -static const unsigned char s6e8ax0_22_gamma_140[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0, - 0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4, - 0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5, -}; - -static const unsigned char s6e8ax0_22_gamma_150[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0, - 0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1, - 0x00, 0x99, 0x00, 0x90, 0x00, 0xba, -}; - -static const unsigned char s6e8ax0_22_gamma_160[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0, - 0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1, - 0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe, -}; - -static const unsigned char s6e8ax0_22_gamma_170[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1, - 0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1, - 0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2, -}; - -static const unsigned char s6e8ax0_22_gamma_180[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2, - 0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1, - 0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5, -}; - -static const unsigned char s6e8ax0_22_gamma_190[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2, - 0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf, - 0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9, -}; - -static const unsigned char s6e8ax0_22_gamma_200[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2, - 0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae, - 0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd, -}; - -static const unsigned char s6e8ax0_22_gamma_210[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1, - 0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad, - 0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4, -}; - -static const unsigned char s6e8ax0_22_gamma_220[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1, - 0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad, - 0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3, -}; - -static const unsigned char s6e8ax0_22_gamma_230[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1, - 0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad, - 0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7, -}; - -static const unsigned char s6e8ax0_22_gamma_240[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2, - 0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab, - 0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb, -}; - -static const unsigned char s6e8ax0_22_gamma_250[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2, - 0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab, - 0x00, 0xb6, 0x00, 0xab, 0x00, 0xde, -}; - -static const unsigned char s6e8ax0_22_gamma_260[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1, - 0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac, - 0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0, -}; - -static const unsigned char s6e8ax0_22_gamma_270[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2, - 0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa, - 0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4, -}; - -static const unsigned char s6e8ax0_22_gamma_280[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0, - 0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9, - 0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7, -}; - -static const unsigned char s6e8ax0_22_gamma_300[] = { - 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2, - 0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9, - 0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed, -}; - -static const unsigned char *s6e8ax0_22_gamma_table[] = { - s6e8ax0_22_gamma_30, - s6e8ax0_22_gamma_50, - s6e8ax0_22_gamma_60, - s6e8ax0_22_gamma_70, - s6e8ax0_22_gamma_80, - s6e8ax0_22_gamma_90, - s6e8ax0_22_gamma_100, - s6e8ax0_22_gamma_120, - s6e8ax0_22_gamma_130, - s6e8ax0_22_gamma_140, - s6e8ax0_22_gamma_150, - s6e8ax0_22_gamma_160, - s6e8ax0_22_gamma_170, - s6e8ax0_22_gamma_180, - s6e8ax0_22_gamma_190, - s6e8ax0_22_gamma_200, - s6e8ax0_22_gamma_210, - s6e8ax0_22_gamma_220, - s6e8ax0_22_gamma_230, - s6e8ax0_22_gamma_240, - s6e8ax0_22_gamma_250, - s6e8ax0_22_gamma_260, - s6e8ax0_22_gamma_270, - s6e8ax0_22_gamma_280, - s6e8ax0_22_gamma_300, -}; - -static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - - static const unsigned char data_to_send[] = { - 0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d, - 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08, - 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0, - 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8 - }; - static const unsigned char data_to_send_panel_reverse[] = { - 0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d, - 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08, - 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0, - 0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1 - }; - - if (lcd->dsim_dev->panel_reverse) - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send_panel_reverse, - ARRAY_SIZE(data_to_send_panel_reverse)); - else - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_display_cond(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xf2, 0x80, 0x03, 0x0d - }; - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -/* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */ -static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - unsigned int gamma = lcd->bd->props.brightness; - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - s6e8ax0_22_gamma_table[gamma], - GAMMA_TABLE_COUNT); -} - -static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xf7, 0x03 - }; - - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_SHORT_WRITE_PARAM, data_to_send, - ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40, - 0x0d, 0x00, 0x00 - }; - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0, - 0x00 - }; - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d - }; - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03 - }; - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02 - }; - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} -static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xe3, 0x40 - }; - - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_SHORT_WRITE_PARAM, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00 - }; - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xb1, 0x04, 0x00 - }; - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11, - 0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed, - 0x64, 0xaf - }; - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0x10, 0x00 - }; - - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_SHORT_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0x11, 0x00 - }; - - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_SHORT_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_display_on(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0x29, 0x00 - }; - - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_SHORT_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_display_off(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0x28, 0x00 - }; - - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_SHORT_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xf0, 0x5a, 0x5a - }; - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_acl_on(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xc0, 0x01 - }; - - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_SHORT_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -static void s6e8ax0_acl_off(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - static const unsigned char data_to_send[] = { - 0xc0, 0x00 - }; - - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_SHORT_WRITE, - data_to_send, ARRAY_SIZE(data_to_send)); -} - -/* Full white 50% reducing setting */ -static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - /* Full white 50% reducing setting */ - static const unsigned char cutoff_50[] = { - 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, - 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38, - 0x3f, 0x46 - }; - /* Full white 45% reducing setting */ - static const unsigned char cutoff_45[] = { - 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, - 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31, - 0x37, 0x3d - }; - /* Full white 40% reducing setting */ - static const unsigned char cutoff_40[] = { - 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf, - 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b, - 0x31, 0x36 - }; - - if (lcd->acl_enable) { - if (lcd->cur_acl == 0) { - if (lcd->gamma == 0 || lcd->gamma == 1) { - s6e8ax0_acl_off(lcd); - dev_dbg(&lcd->ld->dev, - "cur_acl=%d\n", lcd->cur_acl); - } else - s6e8ax0_acl_on(lcd); - } - switch (lcd->gamma) { - case 0: /* 30cd */ - s6e8ax0_acl_off(lcd); - lcd->cur_acl = 0; - break; - case 1 ... 3: /* 50cd ~ 90cd */ - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_LONG_WRITE, - cutoff_40, - ARRAY_SIZE(cutoff_40)); - lcd->cur_acl = 40; - break; - case 4 ... 7: /* 120cd ~ 210cd */ - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_LONG_WRITE, - cutoff_45, - ARRAY_SIZE(cutoff_45)); - lcd->cur_acl = 45; - break; - case 8 ... 10: /* 220cd ~ 300cd */ - ops->cmd_write(lcd_to_master(lcd), - MIPI_DSI_DCS_LONG_WRITE, - cutoff_50, - ARRAY_SIZE(cutoff_50)); - lcd->cur_acl = 50; - break; - default: - break; - } - } else { - s6e8ax0_acl_off(lcd); - lcd->cur_acl = 0; - dev_dbg(&lcd->ld->dev, "cur_acl = %d\n", lcd->cur_acl); - } -} - -static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id) -{ - unsigned int ret; - unsigned int addr = 0xd1; /* MTP ID */ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - - ret = ops->cmd_read(lcd_to_master(lcd), - MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM, - addr, 3, mtp_id); -} - -static int s6e8ax0_panel_init(struct s6e8ax0 *lcd) -{ - s6e8ax0_apply_level2_key(lcd); - s6e8ax0_sleep_out(lcd); - msleep(1); - s6e8ax0_panel_cond(lcd); - s6e8ax0_display_cond(lcd); - s6e8ax0_gamma_cond(lcd); - s6e8ax0_gamma_update(lcd); - - s6e8ax0_etc_cond1(lcd); - s6e8ax0_etc_cond2(lcd); - s6e8ax0_etc_cond3(lcd); - s6e8ax0_etc_cond4(lcd); - s6e8ax0_etc_cond5(lcd); - s6e8ax0_etc_cond6(lcd); - s6e8ax0_etc_cond7(lcd); - - s6e8ax0_elvss_nvm_set(lcd); - s6e8ax0_elvss_set(lcd); - - s6e8ax0_acl_ctrl_set(lcd); - s6e8ax0_acl_on(lcd); - - /* if ID3 value is not 33h, branch private elvss mode */ - msleep(lcd->ddi_pd->power_on_delay); - - return 0; -} - -static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness) -{ - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - - ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE, - s6e8ax0_22_gamma_table[brightness], - ARRAY_SIZE(s6e8ax0_22_gamma_table)); - - /* update gamma table. */ - s6e8ax0_gamma_update(lcd); - lcd->gamma = brightness; - - return 0; -} - -static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma) -{ - s6e8ax0_update_gamma_ctrl(lcd, gamma); - - return 0; -} - -static int s6e8ax0_set_power(struct lcd_device *ld, int power) -{ - struct s6e8ax0 *lcd = lcd_get_data(ld); - struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd); - int ret = 0; - - if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN && - power != FB_BLANK_NORMAL) { - dev_err(lcd->dev, "power value should be 0, 1 or 4.\n"); - return -EINVAL; - } - - if ((power == FB_BLANK_UNBLANK) && ops->set_blank_mode) { - /* LCD power on */ - if ((POWER_IS_ON(power) && POWER_IS_OFF(lcd->power)) - || (POWER_IS_ON(power) && POWER_IS_NRM(lcd->power))) { - ret = ops->set_blank_mode(lcd_to_master(lcd), power); - if (!ret && lcd->power != power) - lcd->power = power; - } - } else if ((power == FB_BLANK_POWERDOWN) && ops->set_early_blank_mode) { - /* LCD power off */ - if ((POWER_IS_OFF(power) && POWER_IS_ON(lcd->power)) || - (POWER_IS_ON(lcd->power) && POWER_IS_NRM(power))) { - ret = ops->set_early_blank_mode(lcd_to_master(lcd), - power); - if (!ret && lcd->power != power) - lcd->power = power; - } - } - - return ret; -} - -static int s6e8ax0_get_power(struct lcd_device *ld) -{ - struct s6e8ax0 *lcd = lcd_get_data(ld); - - return lcd->power; -} - -static int s6e8ax0_set_brightness(struct backlight_device *bd) -{ - int ret = 0, brightness = bd->props.brightness; - struct s6e8ax0 *lcd = bl_get_data(bd); - - if (brightness < MIN_BRIGHTNESS || - brightness > bd->props.max_brightness) { - dev_err(lcd->dev, "lcd brightness should be %d to %d.\n", - MIN_BRIGHTNESS, MAX_BRIGHTNESS); - return -EINVAL; - } - - ret = s6e8ax0_gamma_ctrl(lcd, brightness); - if (ret) { - dev_err(&bd->dev, "lcd brightness setting failed.\n"); - return -EIO; - } - - return ret; -} - -static struct lcd_ops s6e8ax0_lcd_ops = { - .set_power = s6e8ax0_set_power, - .get_power = s6e8ax0_get_power, -}; - -static const struct backlight_ops s6e8ax0_backlight_ops = { - .update_status = s6e8ax0_set_brightness, -}; - -static void s6e8ax0_power_on(struct mipi_dsim_lcd_device *dsim_dev, int power) -{ - struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); - - msleep(lcd->ddi_pd->power_on_delay); - - /* lcd power on */ - if (power) - s6e8ax0_regulator_enable(lcd); - else - s6e8ax0_regulator_disable(lcd); - - msleep(lcd->ddi_pd->reset_delay); - - /* lcd reset */ - if (lcd->ddi_pd->reset) - lcd->ddi_pd->reset(lcd->ld); - msleep(5); -} - -static void s6e8ax0_set_sequence(struct mipi_dsim_lcd_device *dsim_dev) -{ - struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); - - s6e8ax0_panel_init(lcd); - s6e8ax0_display_on(lcd); - - lcd->power = FB_BLANK_UNBLANK; -} - -static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev) -{ - struct s6e8ax0 *lcd; - int ret; - u8 mtp_id[3] = {0, }; - - lcd = devm_kzalloc(&dsim_dev->dev, sizeof(struct s6e8ax0), GFP_KERNEL); - if (!lcd) { - dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n"); - return -ENOMEM; - } - - lcd->dsim_dev = dsim_dev; - lcd->ddi_pd = (struct lcd_platform_data *)dsim_dev->platform_data; - lcd->dev = &dsim_dev->dev; - - mutex_init(&lcd->lock); - - ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies); - if (ret) { - dev_err(lcd->dev, "Failed to get regulators: %d\n", ret); - return ret; - } - - lcd->ld = devm_lcd_device_register(lcd->dev, "s6e8ax0", lcd->dev, lcd, - &s6e8ax0_lcd_ops); - if (IS_ERR(lcd->ld)) { - dev_err(lcd->dev, "failed to register lcd ops.\n"); - return PTR_ERR(lcd->ld); - } - - lcd->bd = devm_backlight_device_register(lcd->dev, "s6e8ax0-bl", - lcd->dev, lcd, &s6e8ax0_backlight_ops, NULL); - if (IS_ERR(lcd->bd)) { - dev_err(lcd->dev, "failed to register backlight ops.\n"); - return PTR_ERR(lcd->bd); - } - - lcd->bd->props.max_brightness = MAX_BRIGHTNESS; - lcd->bd->props.brightness = MAX_BRIGHTNESS; - - s6e8ax0_read_id(lcd, mtp_id); - if (mtp_id[0] == 0x00) - dev_err(lcd->dev, "read id failed\n"); - - dev_info(lcd->dev, "Read ID : %x, %x, %x\n", - mtp_id[0], mtp_id[1], mtp_id[2]); - - if (mtp_id[2] == 0x33) - dev_info(lcd->dev, - "ID-3 is 0xff does not support dynamic elvss\n"); - else - dev_info(lcd->dev, - "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]); - - lcd->acl_enable = 1; - lcd->cur_acl = 0; - - dev_set_drvdata(&dsim_dev->dev, lcd); - - dev_dbg(lcd->dev, "probed s6e8ax0 panel driver.\n"); - - return 0; -} - -static int __maybe_unused s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev) -{ - struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); - - s6e8ax0_sleep_in(lcd); - msleep(lcd->ddi_pd->power_off_delay); - s6e8ax0_display_off(lcd); - - s6e8ax0_regulator_disable(lcd); - - return 0; -} - -static int __maybe_unused s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev) -{ - struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev); - - s6e8ax0_sleep_out(lcd); - msleep(lcd->ddi_pd->power_on_delay); - - s6e8ax0_regulator_enable(lcd); - s6e8ax0_set_sequence(dsim_dev); - - return 0; -} - -static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = { - .name = "s6e8ax0", - .id = -1, - - .power_on = s6e8ax0_power_on, - .set_sequence = s6e8ax0_set_sequence, - .probe = s6e8ax0_probe, - .suspend = IS_ENABLED(CONFIG_PM) ? s6e8ax0_suspend : NULL, - .resume = IS_ENABLED(CONFIG_PM) ? s6e8ax0_resume : NULL, -}; - -static int s6e8ax0_init(void) -{ - exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver); - - return 0; -} - -static void s6e8ax0_exit(void) -{ - return; -} - -module_init(s6e8ax0_init); -module_exit(s6e8ax0_exit); - -MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); -MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); -MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbdev/hecubafb.c b/drivers/video/fbdev/hecubafb.c index e4031ef39491..8577195cb533 100644 --- a/drivers/video/fbdev/hecubafb.c +++ b/drivers/video/fbdev/hecubafb.c @@ -47,7 +47,7 @@ #define DPY_W 600 #define DPY_H 800 -static struct fb_fix_screeninfo hecubafb_fix = { +static const struct fb_fix_screeninfo hecubafb_fix = { .id = "hecubafb", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_MONO01, @@ -58,7 +58,7 @@ static struct fb_fix_screeninfo hecubafb_fix = { .accel = FB_ACCEL_NONE, }; -static struct fb_var_screeninfo hecubafb_var = { +static const struct fb_var_screeninfo hecubafb_var = { .xres = DPY_W, .yres = DPY_H, .xres_virtual = DPY_W, diff --git a/drivers/video/fbdev/hgafb.c b/drivers/video/fbdev/hgafb.c index 15d3ccff2965..463028543173 100644 --- a/drivers/video/fbdev/hgafb.c +++ b/drivers/video/fbdev/hgafb.c @@ -106,7 +106,7 @@ static DEFINE_SPINLOCK(hga_reg_lock); /* Framebuffer driver structures */ -static struct fb_var_screeninfo hga_default_var = { +static const struct fb_var_screeninfo hga_default_var = { .xres = 720, .yres = 348, .xres_virtual = 720, diff --git a/drivers/video/fbdev/i740fb.c b/drivers/video/fbdev/i740fb.c index cf5ccd0f2252..7bc5f6056c77 100644 --- a/drivers/video/fbdev/i740fb.c +++ b/drivers/video/fbdev/i740fb.c @@ -82,7 +82,7 @@ struct i740fb_par { #define DACSPEED24_SD 128 #define DACSPEED32 86 -static struct fb_fix_screeninfo i740fb_fix = { +static const struct fb_fix_screeninfo i740fb_fix = { .id = "i740fb", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_TRUECOLOR, diff --git a/drivers/video/fbdev/i810/i810_main.c b/drivers/video/fbdev/i810/i810_main.c index 025b882a4826..483ab2592d0c 100644 --- a/drivers/video/fbdev/i810/i810_main.c +++ b/drivers/video/fbdev/i810/i810_main.c @@ -1691,7 +1691,7 @@ static int i810_alloc_agp_mem(struct fb_info *info) if (!(par->i810_gtt.i810_cursor_memory = agp_allocate_memory(bridge, par->cursor_heap.size >> 12, AGP_PHYSICAL_MEMORY))) { - printk("i810fb_alloc_cursormem: can't allocate" + printk("i810fb_alloc_cursormem: can't allocate " "cursor memory\n"); agp_backend_release(bridge); return -ENOMEM; diff --git a/drivers/video/fbdev/intelfb/intelfbdrv.c b/drivers/video/fbdev/intelfb/intelfbdrv.c index bf207444ba0c..ff2a5d2023e1 100644 --- a/drivers/video/fbdev/intelfb/intelfbdrv.c +++ b/drivers/video/fbdev/intelfb/intelfbdrv.c @@ -1301,11 +1301,6 @@ static int intelfb_check_var(struct fb_var_screeninfo *var, break; } - if (v.xoffset < 0) - v.xoffset = 0; - if (v.yoffset < 0) - v.yoffset = 0; - if (v.xoffset > v.xres_virtual - v.xres) v.xoffset = v.xres_virtual - v.xres; if (v.yoffset > v.yres_virtual - v.yres) diff --git a/drivers/video/fbdev/kyro/fbdev.c b/drivers/video/fbdev/kyro/fbdev.c index 5bb01533271e..f77478fb3d14 100644 --- a/drivers/video/fbdev/kyro/fbdev.c +++ b/drivers/video/fbdev/kyro/fbdev.c @@ -44,7 +44,7 @@ static struct fb_fix_screeninfo kyro_fix = { .accel = FB_ACCEL_NONE, }; -static struct fb_var_screeninfo kyro_var = { +static const struct fb_var_screeninfo kyro_var = { /* 640x480, 16bpp @ 60 Hz */ .xres = 640, .yres = 480, diff --git a/drivers/video/fbdev/matrox/matroxfb_Ti3026.c b/drivers/video/fbdev/matrox/matroxfb_Ti3026.c index 195ad7cac1ba..68fa037d8cbc 100644 --- a/drivers/video/fbdev/matrox/matroxfb_Ti3026.c +++ b/drivers/video/fbdev/matrox/matroxfb_Ti3026.c @@ -372,7 +372,7 @@ static int Ti3026_init(struct matrox_fb_info *minfo, struct my_timming *m) DBG(__func__) - memcpy(hw->DACreg, MGADACbpp32, sizeof(hw->DACreg)); + memcpy(hw->DACreg, MGADACbpp32, sizeof(MGADACbpp32)); switch (minfo->fbcon.var.bits_per_pixel) { case 4: hw->DACreg[POS3026_XLATCHCTRL] = TVP3026_XLATCHCTRL_16_1; /* or _8_1, they are same */ hw->DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR; diff --git a/drivers/video/fbdev/matrox/matroxfb_g450.c b/drivers/video/fbdev/matrox/matroxfb_g450.c index cff0546ea6fd..f108ae66fc83 100644 --- a/drivers/video/fbdev/matrox/matroxfb_g450.c +++ b/drivers/video/fbdev/matrox/matroxfb_g450.c @@ -433,7 +433,7 @@ static void cve2_init_TVdata(int norm, struct mavenregs* data, const struct outp 0x00, /* 3E written multiple times */ 0x00, /* 3F not written */ } }; - static struct mavenregs ntscregs = { { + static const struct mavenregs ntscregs = { { 0x21, 0xF0, 0x7C, 0x1F, /* 00: chroma subcarrier */ 0x00, 0x00, /* test */ diff --git a/drivers/video/fbdev/mb862xx/mb862xx-i2c.c b/drivers/video/fbdev/mb862xx/mb862xx-i2c.c index c87e17afb3e2..ba96c44f2761 100644 --- a/drivers/video/fbdev/mb862xx/mb862xx-i2c.c +++ b/drivers/video/fbdev/mb862xx/mb862xx-i2c.c @@ -157,17 +157,10 @@ static struct i2c_adapter mb862xx_i2c_adapter = { int mb862xx_i2c_init(struct mb862xxfb_par *par) { - int ret; - mb862xx_i2c_adapter.algo_data = par; par->adap = &mb862xx_i2c_adapter; - ret = i2c_add_adapter(par->adap); - if (ret < 0) { - dev_err(par->dev, "failed to add %s\n", - mb862xx_i2c_adapter.name); - } - return ret; + return i2c_add_adapter(par->adap); } void mb862xx_i2c_exit(struct mb862xxfb_par *par) diff --git a/drivers/video/fbdev/mx3fb.c b/drivers/video/fbdev/mx3fb.c index f91b1db262b0..8778e01cebac 100644 --- a/drivers/video/fbdev/mx3fb.c +++ b/drivers/video/fbdev/mx3fb.c @@ -845,7 +845,7 @@ static int __set_par(struct fb_info *fbi, bool lock) if (fbi->var.sync & FB_SYNC_SHARP_MODE) mode = IPU_PANEL_SHARP_TFT; - dev_dbg(fbi->device, "pixclock = %ul Hz\n", + dev_dbg(fbi->device, "pixclock = %u Hz\n", (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL)); if (sdc_init_panel(mx3fb, mode, diff --git a/drivers/video/fbdev/mxsfb.c b/drivers/video/fbdev/mxsfb.c index 4e6608ceac09..7846f0e8bbbb 100644 --- a/drivers/video/fbdev/mxsfb.c +++ b/drivers/video/fbdev/mxsfb.c @@ -800,6 +800,7 @@ static int mxsfb_init_fbinfo(struct mxsfb_info *host, struct fb_videomode *vmode) { int ret; + struct device *dev = &host->pdev->dev; struct fb_info *fb_info = &host->fb_info; struct fb_var_screeninfo *var = &fb_info->var; dma_addr_t fb_phys; @@ -825,12 +826,10 @@ static int mxsfb_init_fbinfo(struct mxsfb_info *host, /* Memory allocation for framebuffer */ fb_size = SZ_2M; - fb_virt = alloc_pages_exact(fb_size, GFP_DMA); + fb_virt = dma_alloc_wc(dev, PAGE_ALIGN(fb_size), &fb_phys, GFP_KERNEL); if (!fb_virt) return -ENOMEM; - fb_phys = virt_to_phys(fb_virt); - fb_info->fix.smem_start = fb_phys; fb_info->screen_base = fb_virt; fb_info->screen_size = fb_info->fix.smem_len = fb_size; @@ -843,9 +842,11 @@ static int mxsfb_init_fbinfo(struct mxsfb_info *host, static void mxsfb_free_videomem(struct mxsfb_info *host) { + struct device *dev = &host->pdev->dev; struct fb_info *fb_info = &host->fb_info; - free_pages_exact(fb_info->screen_base, fb_info->fix.smem_len); + dma_free_wc(dev, fb_info->screen_size, fb_info->screen_base, + fb_info->fix.smem_start); } static const struct platform_device_id mxsfb_devtype[] = { diff --git a/drivers/video/fbdev/offb.c b/drivers/video/fbdev/offb.c index fb60a8f0cc94..906c6e75c260 100644 --- a/drivers/video/fbdev/offb.c +++ b/drivers/video/fbdev/offb.c @@ -625,6 +625,21 @@ static void __init offb_init_nodriver(struct device_node *dp, int no_real_node) if (address == OF_BAD_ADDR && addr_prop) address = (u64)addr_prop; if (address != OF_BAD_ADDR) { +#ifdef CONFIG_PCI + const __be32 *vidp, *didp; + u32 vid, did; + struct pci_dev *pdev; + + vidp = of_get_property(dp, "vendor-id", NULL); + didp = of_get_property(dp, "device-id", NULL); + if (vidp && didp) { + vid = be32_to_cpup(vidp); + did = be32_to_cpup(didp); + pdev = pci_get_device(vid, did, NULL); + if (!pdev || pci_enable_device(pdev)) + return; + } +#endif /* kludge for valkyrie */ if (strcmp(dp->name, "valkyrie") == 0) address += 0x1000; diff --git a/drivers/video/fbdev/omap/lcd_mipid.c b/drivers/video/fbdev/omap/lcd_mipid.c index 0e4cee9a8d79..c81f150589e1 100644 --- a/drivers/video/fbdev/omap/lcd_mipid.c +++ b/drivers/video/fbdev/omap/lcd_mipid.c @@ -60,7 +60,6 @@ struct mipid_device { struct mutex mutex; struct lcd_panel panel; - struct workqueue_struct *esd_wq; struct delayed_work esd_work; void (*esd_check)(struct mipid_device *m); }; @@ -390,7 +389,7 @@ static void ls041y3_esd_check(struct mipid_device *md) static void mipid_esd_start_check(struct mipid_device *md) { if (md->esd_check != NULL) - queue_delayed_work(md->esd_wq, &md->esd_work, + schedule_delayed_work(&md->esd_work, MIPID_ESD_CHECK_PERIOD); } @@ -476,11 +475,6 @@ static int mipid_init(struct lcd_panel *panel, struct mipid_device *md = to_mipid_device(panel); md->fbdev = fbdev; - md->esd_wq = create_singlethread_workqueue("mipid_esd"); - if (md->esd_wq == NULL) { - dev_err(&md->spi->dev, "can't create ESD workqueue\n"); - return -ENOMEM; - } INIT_DELAYED_WORK(&md->esd_work, mipid_esd_work); mutex_init(&md->mutex); @@ -500,7 +494,6 @@ static void mipid_cleanup(struct lcd_panel *panel) if (md->enabled) mipid_esd_stop_check(md); - destroy_workqueue(md->esd_wq); } static struct lcd_panel mipid_panel = { diff --git a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c index b58012b82b6f..8b810696a42b 100644 --- a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c +++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c @@ -75,8 +75,6 @@ struct panel_drv_data { bool intro_printed; - struct workqueue_struct *workqueue; - bool ulps_enabled; unsigned ulps_timeout; struct delayed_work ulps_work; @@ -232,7 +230,7 @@ static int dsicm_set_update_window(struct panel_drv_data *ddata, static void dsicm_queue_ulps_work(struct panel_drv_data *ddata) { if (ddata->ulps_timeout > 0) - queue_delayed_work(ddata->workqueue, &ddata->ulps_work, + schedule_delayed_work(&ddata->ulps_work, msecs_to_jiffies(ddata->ulps_timeout)); } @@ -1244,11 +1242,6 @@ static int dsicm_probe(struct platform_device *pdev) dev_dbg(dev, "Using GPIO TE\n"); } - ddata->workqueue = create_singlethread_workqueue("dsicm_wq"); - if (ddata->workqueue == NULL) { - dev_err(dev, "can't create workqueue\n"); - return -ENOMEM; - } INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work); dsicm_hw_reset(ddata); @@ -1262,7 +1255,7 @@ static int dsicm_probe(struct platform_device *pdev) dev, ddata, &dsicm_bl_ops, &props); if (IS_ERR(bldev)) { r = PTR_ERR(bldev); - goto err_bl; + goto err_reg; } ddata->bldev = bldev; @@ -1285,8 +1278,6 @@ static int dsicm_probe(struct platform_device *pdev) err_sysfs_create: if (bldev != NULL) backlight_device_unregister(bldev); -err_bl: - destroy_workqueue(ddata->workqueue); err_reg: return r; } @@ -1316,7 +1307,6 @@ static int __exit dsicm_remove(struct platform_device *pdev) omap_dss_put_device(ddata->in); dsicm_cancel_ulps_work(ddata); - destroy_workqueue(ddata->workqueue); /* reset, to be sure that the panel is in a valid state */ dsicm_hw_reset(ddata); diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dispc-compat.c b/drivers/video/fbdev/omap2/omapfb/dss/dispc-compat.c index 3691bde4ce0a..a864608c5df1 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dispc-compat.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dispc-compat.c @@ -644,6 +644,7 @@ int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, { int r; + long time_left; DECLARE_COMPLETION_ONSTACK(completion); r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion, @@ -652,15 +653,15 @@ int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, if (r) return r; - timeout = wait_for_completion_interruptible_timeout(&completion, + time_left = wait_for_completion_interruptible_timeout(&completion, timeout); omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask); - if (timeout == 0) + if (time_left == 0) return -ETIMEDOUT; - if (timeout == -ERESTARTSYS) + if (time_left == -ERESTARTSYS) return -ERESTARTSYS; return 0; diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dsi.c b/drivers/video/fbdev/omap2/omapfb/dss/dsi.c index 9e4800a4e3d1..30d49f3800b3 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dsi.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dsi.c @@ -1167,7 +1167,6 @@ static int dsi_regulator_init(struct platform_device *dsidev) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); struct regulator *vdds_dsi; - int r; if (dsi->vdds_dsi_reg != NULL) return 0; @@ -1180,13 +1179,6 @@ static int dsi_regulator_init(struct platform_device *dsidev) return PTR_ERR(vdds_dsi); } - r = regulator_set_voltage(vdds_dsi, 1800000, 1800000); - if (r) { - devm_regulator_put(vdds_dsi); - DSSERR("can't set the DSI regulator voltage\n"); - return r; - } - dsi->vdds_dsi_reg = vdds_dsi; return 0; @@ -5348,7 +5340,7 @@ static int dsi_bind(struct device *dev, struct device *master, void *data) dsi->phy_base = devm_ioremap(&dsidev->dev, res->start, resource_size(res)); - if (!dsi->proto_base) { + if (!dsi->phy_base) { DSSERR("can't ioremap DSI PHY\n"); return -ENOMEM; } @@ -5368,7 +5360,7 @@ static int dsi_bind(struct device *dev, struct device *master, void *data) dsi->pll_base = devm_ioremap(&dsidev->dev, res->start, resource_size(res)); - if (!dsi->proto_base) { + if (!dsi->pll_base) { DSSERR("can't ioremap DSI PLL\n"); return -ENOMEM; } diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c index 926a6f20dbb2..156a254705ea 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c @@ -100,7 +100,6 @@ static irqreturn_t hdmi_irq_handler(int irq, void *data) static int hdmi_init_regulator(void) { - int r; struct regulator *reg; if (hdmi.vdda_reg != NULL) @@ -114,13 +113,6 @@ static int hdmi_init_regulator(void) return PTR_ERR(reg); } - r = regulator_set_voltage(reg, 1800000, 1800000); - if (r) { - devm_regulator_put(reg); - DSSWARN("can't set the regulator voltage\n"); - return r; - } - hdmi.vdda_reg = reg; return 0; diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi5.c b/drivers/video/fbdev/omap2/omapfb/dss/hdmi5.c index 0ee829a165c3..4da36bcab977 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi5.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi5.c @@ -119,7 +119,6 @@ static irqreturn_t hdmi_irq_handler(int irq, void *data) static int hdmi_init_regulator(void) { - int r; struct regulator *reg; if (hdmi.vdda_reg != NULL) @@ -131,13 +130,6 @@ static int hdmi_init_regulator(void) return PTR_ERR(reg); } - r = regulator_set_voltage(reg, 1800000, 1800000); - if (r) { - devm_regulator_put(reg); - DSSWARN("can't set the regulator voltage\n"); - return r; - } - hdmi.vdda_reg = reg; return 0; diff --git a/drivers/video/fbdev/pm2fb.c b/drivers/video/fbdev/pm2fb.c index aa8d28880912..1a4070f719c2 100644 --- a/drivers/video/fbdev/pm2fb.c +++ b/drivers/video/fbdev/pm2fb.c @@ -113,7 +113,7 @@ static struct fb_fix_screeninfo pm2fb_fix = { /* * Default video mode. In case the modedb doesn't work. */ -static struct fb_var_screeninfo pm2fb_var = { +static const struct fb_var_screeninfo pm2fb_var = { /* "640x480, 8 bpp @ 60 Hz */ .xres = 640, .yres = 480, diff --git a/drivers/video/fbdev/pxafb.c b/drivers/video/fbdev/pxafb.c index 2c0487f4f805..ef73f14d7ba0 100644 --- a/drivers/video/fbdev/pxafb.c +++ b/drivers/video/fbdev/pxafb.c @@ -2125,7 +2125,7 @@ static int of_get_pxafb_display(struct device *dev, struct device_node *disp, timings = of_get_display_timings(disp); if (!timings) - goto out; + return -EINVAL; ret = -ENOMEM; info->modes = kmalloc_array(timings->num_timings, @@ -2186,6 +2186,7 @@ static int of_get_pxafb_mode_info(struct device *dev, ret = of_property_read_u32(np, "bus-width", &bus_width); if (ret) { dev_err(dev, "no bus-width specified: %d\n", ret); + of_node_put(np); return ret; } diff --git a/drivers/video/fbdev/s1d13xxxfb.c b/drivers/video/fbdev/s1d13xxxfb.c index 96aa46dc696c..5d6179ef0298 100644 --- a/drivers/video/fbdev/s1d13xxxfb.c +++ b/drivers/video/fbdev/s1d13xxxfb.c @@ -83,7 +83,7 @@ static const char *s1d13xxxfb_prod_names[] = { /* * here we define the default struct fb_fix_screeninfo */ -static struct fb_fix_screeninfo s1d13xxxfb_fix = { +static const struct fb_fix_screeninfo s1d13xxxfb_fix = { .id = S1D_FBID, .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_PSEUDOCOLOR, @@ -929,7 +929,7 @@ static int s1d13xxxfb_suspend(struct platform_device *dev, pm_message_t state) s1dfb->disp_save = kmalloc(info->fix.smem_len, GFP_KERNEL); if (!s1dfb->disp_save) { - printk(KERN_ERR PFX "no memory to save screen"); + printk(KERN_ERR PFX "no memory to save screen\n"); return -ENOMEM; } diff --git a/drivers/video/fbdev/s3c2410fb.c b/drivers/video/fbdev/s3c2410fb.c index 0dd86be36afb..a67e4567e656 100644 --- a/drivers/video/fbdev/s3c2410fb.c +++ b/drivers/video/fbdev/s3c2410fb.c @@ -767,7 +767,7 @@ static irqreturn_t s3c2410fb_irq(int irq, void *dev_id) return IRQ_HANDLED; } -#ifdef CONFIG_CPU_FREQ +#ifdef CONFIG_ARM_S3C24XX_CPUFREQ static int s3c2410fb_cpufreq_transition(struct notifier_block *nb, unsigned long val, void *data) diff --git a/drivers/video/fbdev/s3c2410fb.h b/drivers/video/fbdev/s3c2410fb.h index 47a17bd23011..cdd11e2f8859 100644 --- a/drivers/video/fbdev/s3c2410fb.h +++ b/drivers/video/fbdev/s3c2410fb.h @@ -32,7 +32,7 @@ struct s3c2410fb_info { unsigned long clk_rate; unsigned int palette_ready; -#ifdef CONFIG_CPU_FREQ +#ifdef CONFIG_ARM_S3C24XX_CPUFREQ struct notifier_block freq_transition; #endif diff --git a/drivers/video/fbdev/savage/savagefb_driver.c b/drivers/video/fbdev/savage/savagefb_driver.c index 6c77ab09b0b2..c30a91c1137c 100644 --- a/drivers/video/fbdev/savage/savagefb_driver.c +++ b/drivers/video/fbdev/savage/savagefb_driver.c @@ -1660,7 +1660,7 @@ static struct fb_ops savagefb_ops = { /* --------------------------------------------------------------------- */ -static struct fb_var_screeninfo savagefb_var800x600x8 = { +static const struct fb_var_screeninfo savagefb_var800x600x8 = { .accel_flags = FB_ACCELF_TEXT, .xres = 800, .yres = 600, diff --git a/drivers/video/fbdev/simplefb.c b/drivers/video/fbdev/simplefb.c index e9cf19977285..61f799a515dc 100644 --- a/drivers/video/fbdev/simplefb.c +++ b/drivers/video/fbdev/simplefb.c @@ -33,14 +33,14 @@ #include <linux/parser.h> #include <linux/regulator/consumer.h> -static struct fb_fix_screeninfo simplefb_fix = { +static const struct fb_fix_screeninfo simplefb_fix = { .id = "simple", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_TRUECOLOR, .accel = FB_ACCEL_NONE, }; -static struct fb_var_screeninfo simplefb_var = { +static const struct fb_var_screeninfo simplefb_var = { .height = -1, .width = -1, .activate = FB_ACTIVATE_NOW, @@ -74,8 +74,14 @@ static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, return 0; } +struct simplefb_par; +static void simplefb_clocks_destroy(struct simplefb_par *par); +static void simplefb_regulators_destroy(struct simplefb_par *par); + static void simplefb_destroy(struct fb_info *info) { + simplefb_regulators_destroy(info->par); + simplefb_clocks_destroy(info->par); if (info->screen_base) iounmap(info->screen_base); } @@ -487,11 +493,8 @@ error_fb_release: static int simplefb_remove(struct platform_device *pdev) { struct fb_info *info = platform_get_drvdata(pdev); - struct simplefb_par *par = info->par; unregister_framebuffer(info); - simplefb_regulators_destroy(par); - simplefb_clocks_destroy(par); framebuffer_release(info); return 0; diff --git a/drivers/video/fbdev/sm712fb.c b/drivers/video/fbdev/sm712fb.c index 86ae1d4556fc..73cb4ffff3c5 100644 --- a/drivers/video/fbdev/sm712fb.c +++ b/drivers/video/fbdev/sm712fb.c @@ -56,7 +56,7 @@ struct smtcfb_info { void __iomem *smtc_regbaseaddress; /* Memory Map IO starting address */ -static struct fb_var_screeninfo smtcfb_var = { +static const struct fb_var_screeninfo smtcfb_var = { .xres = 1024, .yres = 600, .xres_virtual = 1024, diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c index 9279e5f6696e..ec2e7e353685 100644 --- a/drivers/video/fbdev/smscufx.c +++ b/drivers/video/fbdev/smscufx.c @@ -1761,10 +1761,8 @@ error: static void ufx_usb_disconnect(struct usb_interface *interface) { struct ufx_data *dev; - struct fb_info *info; dev = usb_get_intfdata(interface); - info = dev->info; pr_debug("USB disconnect starting\n"); diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index a9c45c89b15e..2925d5ce8d3e 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -64,7 +64,7 @@ struct ssd1307fb_par { u32 contrast; u32 dclk_div; u32 dclk_frq; - struct ssd1307fb_deviceinfo *device_info; + const struct ssd1307fb_deviceinfo *device_info; struct i2c_client *client; u32 height; struct fb_info *info; @@ -84,7 +84,7 @@ struct ssd1307fb_array { u8 data[0]; }; -static struct fb_fix_screeninfo ssd1307fb_fix = { +static const struct fb_fix_screeninfo ssd1307fb_fix = { .id = "Solomon SSD1307", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_MONO10, @@ -94,7 +94,7 @@ static struct fb_fix_screeninfo ssd1307fb_fix = { .accel = FB_ACCEL_NONE, }; -static struct fb_var_screeninfo ssd1307fb_var = { +static const struct fb_var_screeninfo ssd1307fb_var = { .bits_per_pixel = 1, }; @@ -559,8 +559,7 @@ static int ssd1307fb_probe(struct i2c_client *client, par->info = info; par->client = client; - par->device_info = (struct ssd1307fb_deviceinfo *)of_match_device( - ssd1307fb_of_match, &client->dev)->data; + par->device_info = of_device_get_match_data(&client->dev); par->reset = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0); diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c index 621fa441a6db..d5fa313806fe 100644 --- a/drivers/video/fbdev/tdfxfb.c +++ b/drivers/video/fbdev/tdfxfb.c @@ -82,7 +82,7 @@ #define VOODOO3_MAX_PIXCLOCK 300000 #define VOODOO5_MAX_PIXCLOCK 350000 -static struct fb_fix_screeninfo tdfx_fix = { +static const struct fb_fix_screeninfo tdfx_fix = { .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_PSEUDOCOLOR, .ypanstep = 1, @@ -90,7 +90,7 @@ static struct fb_fix_screeninfo tdfx_fix = { .accel = FB_ACCEL_3DFX_BANSHEE }; -static struct fb_var_screeninfo tdfx_var = { +static const struct fb_var_screeninfo tdfx_var = { /* "640x480, 8 bpp @ 60 Hz */ .xres = 640, .yres = 480, diff --git a/drivers/video/fbdev/uvesafb.c b/drivers/video/fbdev/uvesafb.c index 178ae93b7ebd..98af9e02959b 100644 --- a/drivers/video/fbdev/uvesafb.c +++ b/drivers/video/fbdev/uvesafb.c @@ -33,7 +33,7 @@ static struct cb_id uvesafb_cn_id = { static char v86d_path[PATH_MAX] = "/sbin/v86d"; static char v86d_started; /* has v86d been started by uvesafb? */ -static struct fb_fix_screeninfo uvesafb_fix = { +static const struct fb_fix_screeninfo uvesafb_fix = { .id = "VESA VGA", .type = FB_TYPE_PACKED_PIXELS, .accel = FB_ACCEL_NONE, diff --git a/drivers/video/fbdev/vfb.c b/drivers/video/fbdev/vfb.c index b9c2f81fb6b9..da653a080394 100644 --- a/drivers/video/fbdev/vfb.c +++ b/drivers/video/fbdev/vfb.c @@ -35,76 +35,23 @@ static void *videomemory; static u_long videomemorysize = VIDEOMEMSIZE; module_param(videomemorysize, ulong, 0); +MODULE_PARM_DESC(videomemorysize, "RAM available to frame buffer (in bytes)"); -/********************************************************************** - * - * Memory management - * - **********************************************************************/ -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - /* - * VFB must clear memory to prevent kernel info - * leakage into userspace - * VGA-based drivers MUST NOT clear memory if - * they want to be able to take over vgacon - */ - - memset(mem, 0, size); - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} +static char *mode_option = NULL; +module_param(mode_option, charp, 0); +MODULE_PARM_DESC(mode_option, "Preferred video mode (e.g. 640x480-8@60)"); -static struct fb_var_screeninfo vfb_default = { +static const struct fb_videomode vfb_default = { .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 }, - .activate = FB_ACTIVATE_TEST, - .height = -1, - .width = -1, - .pixclock = 20000, - .left_margin = 64, - .right_margin = 64, - .upper_margin = 32, - .lower_margin = 32, - .hsync_len = 64, - .vsync_len = 2, - .vmode = FB_VMODE_NONINTERLACED, + .pixclock = 20000, + .left_margin = 64, + .right_margin = 64, + .upper_margin = 32, + .lower_margin = 32, + .hsync_len = 64, + .vsync_len = 2, + .vmode = FB_VMODE_NONINTERLACED, }; static struct fb_fix_screeninfo vfb_fix = { @@ -119,6 +66,7 @@ static struct fb_fix_screeninfo vfb_fix = { static bool vfb_enable __initdata = 0; /* disabled by default */ module_param(vfb_enable, bool, 0); +MODULE_PARM_DESC(vfb_enable, "Enable Virtual FB driver"); static int vfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info); @@ -421,35 +369,7 @@ static int vfb_pan_display(struct fb_var_screeninfo *var, static int vfb_mmap(struct fb_info *info, struct vm_area_struct *vma) { - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end - vma->vm_start; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - unsigned long page, pos; - - if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) - return -EINVAL; - if (size > info->fix.smem_len) - return -EINVAL; - if (offset > info->fix.smem_len - size) - return -EINVAL; - - pos = (unsigned long)info->fix.smem_start + offset; - - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - return 0; - + return remap_vmalloc_range(vma, (void *)info->fix.smem_start, vma->vm_pgoff); } #ifndef MODULE @@ -477,6 +397,8 @@ static int __init vfb_setup(char *options) /* Test disable for backwards compatibility */ if (!strcmp(this_opt, "disable")) vfb_enable = 0; + else + mode_option = this_opt; } return 1; } @@ -489,12 +411,13 @@ static int __init vfb_setup(char *options) static int vfb_probe(struct platform_device *dev) { struct fb_info *info; + unsigned int size = PAGE_ALIGN(videomemorysize); int retval = -ENOMEM; /* * For real video cards we use ioremap. */ - if (!(videomemory = rvmalloc(videomemorysize))) + if (!(videomemory = vmalloc_32_user(size))) return retval; info = framebuffer_alloc(sizeof(u32) * 256, &dev->dev); @@ -504,11 +427,13 @@ static int vfb_probe(struct platform_device *dev) info->screen_base = (char __iomem *)videomemory; info->fbops = &vfb_ops; - retval = fb_find_mode(&info->var, info, NULL, - NULL, 0, NULL, 8); + if (!fb_find_mode(&info->var, info, mode_option, + NULL, 0, &vfb_default, 8)){ + fb_err(info, "Unable to find usable video mode.\n"); + retval = -EINVAL; + goto err1; + } - if (!retval || (retval == 4)) - info->var = vfb_default; vfb_fix.smem_start = (unsigned long) videomemory; vfb_fix.smem_len = videomemorysize; info->fix = vfb_fix; @@ -533,7 +458,7 @@ err2: err1: framebuffer_release(info); err: - rvfree(videomemory, videomemorysize); + vfree(videomemory); return retval; } @@ -543,7 +468,7 @@ static int vfb_remove(struct platform_device *dev) if (info) { unregister_framebuffer(info); - rvfree(videomemory, videomemorysize); + vfree(videomemory); fb_dealloc_cmap(&info->cmap); framebuffer_release(info); } diff --git a/drivers/video/fbdev/vga16fb.c b/drivers/video/fbdev/vga16fb.c index 283d335a759f..5f0690c8fc93 100644 --- a/drivers/video/fbdev/vga16fb.c +++ b/drivers/video/fbdev/vga16fb.c @@ -85,7 +85,7 @@ static struct fb_var_screeninfo vga16fb_defined = { }; /* name should not depend on EGA/VGA */ -static struct fb_fix_screeninfo vga16fb_fix = { +static const struct fb_fix_screeninfo vga16fb_fix = { .id = "VGA16 VGA", .smem_start = VGA_FB_PHYS, .smem_len = VGA_FB_PHYS_LEN, diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 50dbaa805658..fdd3228e0678 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1844,4 +1844,53 @@ config USBPCWATCHDOG Most people will say N. +comment "Watchdog Pretimeout Governors" + +config WATCHDOG_PRETIMEOUT_GOV + bool "Enable watchdog pretimeout governors" + help + The option allows to select watchdog pretimeout governors. + +if WATCHDOG_PRETIMEOUT_GOV + +choice + prompt "Default Watchdog Pretimeout Governor" + default WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC + help + This option selects a default watchdog pretimeout governor. + The governor takes its action, if a watchdog is capable + to report a pretimeout event. + +config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP + bool "noop" + select WATCHDOG_PRETIMEOUT_GOV_NOOP + help + Use noop watchdog pretimeout governor by default. If noop + governor is selected by a user, write a short message to + the kernel log buffer and don't do any system changes. + +config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC + bool "panic" + select WATCHDOG_PRETIMEOUT_GOV_PANIC + help + Use panic watchdog pretimeout governor by default, if + a watchdog pretimeout event happens, consider that + a watchdog feeder is dead and reboot is unavoidable. + +endchoice + +config WATCHDOG_PRETIMEOUT_GOV_NOOP + tristate "Noop watchdog pretimeout governor" + help + Noop watchdog pretimeout governor, only an informational + message is added to kernel log buffer. + +config WATCHDOG_PRETIMEOUT_GOV_PANIC + tristate "Panic watchdog pretimeout governor" + help + Panic watchdog pretimeout governor, on watchdog pretimeout + event put the kernel into panic. + +endif # WATCHDOG_PRETIMEOUT_GOV + endif # WATCHDOG diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index cba00430151b..caa9f4aa492a 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -3,9 +3,15 @@ # # The WatchDog Timer Driver Core. -watchdog-objs += watchdog_core.o watchdog_dev.o obj-$(CONFIG_WATCHDOG_CORE) += watchdog.o +watchdog-objs += watchdog_core.o watchdog_dev.o + +watchdog-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV) += watchdog_pretimeout.o + +obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP) += pretimeout_noop.o +obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC) += pretimeout_panic.o + # Only one watchdog can succeed. We probe the ISA/PCI/USB based # watchdog-cards first, then the architecture specific watchdog # drivers and then the architecture independent "softdog" driver. diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c index c9686b2fdafd..d0b59ba0f661 100644 --- a/drivers/watchdog/asm9260_wdt.c +++ b/drivers/watchdog/asm9260_wdt.c @@ -389,7 +389,6 @@ MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match); static struct platform_driver asm9260_wdt_driver = { .driver = { .name = "asm9260-wdt", - .owner = THIS_MODULE, .of_match_table = asm9260_wdt_of_match, }, .probe = asm9260_wdt_probe, diff --git a/drivers/watchdog/ath79_wdt.c b/drivers/watchdog/ath79_wdt.c index 835d310081e1..e2209bf5fa8a 100644 --- a/drivers/watchdog/ath79_wdt.c +++ b/drivers/watchdog/ath79_wdt.c @@ -35,6 +35,7 @@ #include <linux/err.h> #include <linux/of.h> #include <linux/of_platform.h> +#include <linux/uaccess.h> #define DRIVER_NAME "ath79-wdt" diff --git a/drivers/watchdog/bcm7038_wdt.c b/drivers/watchdog/bcm7038_wdt.c index 4245b65d645c..e238df4d75a2 100644 --- a/drivers/watchdog/bcm7038_wdt.c +++ b/drivers/watchdog/bcm7038_wdt.c @@ -107,7 +107,7 @@ static struct watchdog_info bcm7038_wdt_info = { WDIOF_MAGICCLOSE }; -static struct watchdog_ops bcm7038_wdt_ops = { +static const struct watchdog_ops bcm7038_wdt_ops = { .owner = THIS_MODULE, .start = bcm7038_wdt_start, .stop = bcm7038_wdt_stop, diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c index 4dda9024e229..98acef72334d 100644 --- a/drivers/watchdog/cadence_wdt.c +++ b/drivers/watchdog/cadence_wdt.c @@ -269,7 +269,7 @@ static struct watchdog_info cdns_wdt_info = { }; /* Watchdog Core Ops */ -static struct watchdog_ops cdns_wdt_ops = { +static const struct watchdog_ops cdns_wdt_ops = { .owner = THIS_MODULE, .start = cdns_wdt_start, .stop = cdns_wdt_stop, @@ -424,8 +424,10 @@ static int __maybe_unused cdns_wdt_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct cdns_wdt *wdt = platform_get_drvdata(pdev); - cdns_wdt_stop(&wdt->cdns_wdt_device); - clk_disable_unprepare(wdt->clk); + if (watchdog_active(&wdt->cdns_wdt_device)) { + cdns_wdt_stop(&wdt->cdns_wdt_device); + clk_disable_unprepare(wdt->clk); + } return 0; } @@ -442,12 +444,14 @@ static int __maybe_unused cdns_wdt_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct cdns_wdt *wdt = platform_get_drvdata(pdev); - ret = clk_prepare_enable(wdt->clk); - if (ret) { - dev_err(dev, "unable to enable clock\n"); - return ret; + if (watchdog_active(&wdt->cdns_wdt_device)) { + ret = clk_prepare_enable(wdt->clk); + if (ret) { + dev_err(dev, "unable to enable clock\n"); + return ret; + } + cdns_wdt_start(&wdt->cdns_wdt_device); } - cdns_wdt_start(&wdt->cdns_wdt_device); return 0; } diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index 2acb51cf5504..3c6a3de13a1b 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -54,6 +54,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " struct dw_wdt { void __iomem *regs; struct clk *clk; + unsigned long rate; struct notifier_block restart_handler; struct watchdog_device wdd; }; @@ -72,7 +73,7 @@ static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top) * There are 16 possible timeout values in 0..15 where the number of * cycles is 2 ^ (16 + i) and the watchdog counts down. */ - return (1U << (16 + top)) / clk_get_rate(dw_wdt->clk); + return (1U << (16 + top)) / dw_wdt->rate; } static int dw_wdt_get_top(struct dw_wdt *dw_wdt) @@ -163,7 +164,7 @@ static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd) struct dw_wdt *dw_wdt = to_dw_wdt(wdd); return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) / - clk_get_rate(dw_wdt->clk); + dw_wdt->rate; } static const struct watchdog_info dw_wdt_ident = { @@ -231,6 +232,12 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) if (ret) return ret; + dw_wdt->rate = clk_get_rate(dw_wdt->clk); + if (dw_wdt->rate == 0) { + ret = -EINVAL; + goto out_disable_clk; + } + wdd = &dw_wdt->wdd; wdd->info = &dw_wdt_ident; wdd->ops = &dw_wdt_ops; diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 8f89bd8a826a..70c7194e2810 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -39,7 +39,7 @@ #include <asm/nmi.h> #include <asm/frame.h> -#define HPWDT_VERSION "1.3.3" +#define HPWDT_VERSION "1.4.0" #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) @@ -814,7 +814,8 @@ static int hpwdt_init_one(struct pci_dev *dev, * not run on a legacy ASM box. * So we only support the G5 ProLiant servers and higher. */ - if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) { + if (dev->subsystem_vendor != PCI_VENDOR_ID_HP && + dev->subsystem_vendor != PCI_VENDOR_ID_HP_3PAR) { dev_warn(&dev->dev, "This server does not have an iLO2+ ASIC.\n"); return -ENODEV; @@ -823,7 +824,8 @@ static int hpwdt_init_one(struct pci_dev *dev, /* * Ignore all auxilary iLO devices with the following PCI ID */ - if (dev->subsystem_device == 0x1979) + if (dev->subsystem_vendor == PCI_VENDOR_ID_HP && + dev->subsystem_device == 0x1979) return -ENODEV; if (pci_enable_device(dev)) { diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 54cab189a763..06fcb6c8c917 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -629,7 +629,7 @@ static int iTCO_wdt_resume_noirq(struct device *dev) return 0; } -static struct dev_pm_ops iTCO_wdt_pm = { +static const struct dev_pm_ops iTCO_wdt_pm = { .suspend_noirq = iTCO_wdt_suspend_noirq, .resume_noirq = iTCO_wdt_resume_noirq, }; diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 62f346bb4348..4874b0f18650 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -24,6 +24,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> @@ -37,18 +38,23 @@ #define IMX2_WDT_WCR 0x00 /* Control Register */ #define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */ -#define IMX2_WDT_WCR_WDA (1 << 5) /* -> External Reset WDOG_B */ -#define IMX2_WDT_WCR_SRS (1 << 4) /* -> Software Reset Signal */ -#define IMX2_WDT_WCR_WRE (1 << 3) /* -> WDOG Reset Enable */ -#define IMX2_WDT_WCR_WDE (1 << 2) /* -> Watchdog Enable */ -#define IMX2_WDT_WCR_WDZST (1 << 0) /* -> Watchdog timer Suspend */ +#define IMX2_WDT_WCR_WDA BIT(5) /* -> External Reset WDOG_B */ +#define IMX2_WDT_WCR_SRS BIT(4) /* -> Software Reset Signal */ +#define IMX2_WDT_WCR_WRE BIT(3) /* -> WDOG Reset Enable */ +#define IMX2_WDT_WCR_WDE BIT(2) /* -> Watchdog Enable */ +#define IMX2_WDT_WCR_WDZST BIT(0) /* -> Watchdog timer Suspend */ #define IMX2_WDT_WSR 0x02 /* Service Register */ #define IMX2_WDT_SEQ1 0x5555 /* -> service sequence 1 */ #define IMX2_WDT_SEQ2 0xAAAA /* -> service sequence 2 */ #define IMX2_WDT_WRSR 0x04 /* Reset Status Register */ -#define IMX2_WDT_WRSR_TOUT (1 << 1) /* -> Reset due to Timeout */ +#define IMX2_WDT_WRSR_TOUT BIT(1) /* -> Reset due to Timeout */ + +#define IMX2_WDT_WICR 0x06 /* Interrupt Control Register */ +#define IMX2_WDT_WICR_WIE BIT(15) /* -> Interrupt Enable */ +#define IMX2_WDT_WICR_WTIS BIT(14) /* -> Interrupt Status */ +#define IMX2_WDT_WICR_WICT 0xFF /* -> Interrupt Count Timeout */ #define IMX2_WDT_WMCR 0x08 /* Misc Register */ @@ -80,6 +86,12 @@ static const struct watchdog_info imx2_wdt_info = { .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, }; +static const struct watchdog_info imx2_wdt_pretimeout_info = { + .identity = "imx2+ watchdog", + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | + WDIOF_PRETIMEOUT, +}; + static int imx2_wdt_restart(struct watchdog_device *wdog, unsigned long action, void *data) { @@ -169,6 +181,35 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog, return 0; } +static int imx2_wdt_set_pretimeout(struct watchdog_device *wdog, + unsigned int new_pretimeout) +{ + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + + if (new_pretimeout >= IMX2_WDT_MAX_TIME) + return -EINVAL; + + wdog->pretimeout = new_pretimeout; + + regmap_update_bits(wdev->regmap, IMX2_WDT_WICR, + IMX2_WDT_WICR_WIE | IMX2_WDT_WICR_WICT, + IMX2_WDT_WICR_WIE | (new_pretimeout << 1)); + return 0; +} + +static irqreturn_t imx2_wdt_isr(int irq, void *wdog_arg) +{ + struct watchdog_device *wdog = wdog_arg; + struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); + + regmap_write_bits(wdev->regmap, IMX2_WDT_WICR, + IMX2_WDT_WICR_WTIS, IMX2_WDT_WICR_WTIS); + + watchdog_notify_pretimeout(wdog); + + return IRQ_HANDLED; +} + static int imx2_wdt_start(struct watchdog_device *wdog) { struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); @@ -188,6 +229,7 @@ static const struct watchdog_ops imx2_wdt_ops = { .start = imx2_wdt_start, .ping = imx2_wdt_ping, .set_timeout = imx2_wdt_set_timeout, + .set_pretimeout = imx2_wdt_set_pretimeout, .restart = imx2_wdt_restart, }; @@ -236,6 +278,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000; wdog->parent = &pdev->dev; + ret = platform_get_irq(pdev, 0); + if (ret > 0) + if (!devm_request_irq(&pdev->dev, ret, imx2_wdt_isr, 0, + dev_name(&pdev->dev), wdog)) + wdog->info = &imx2_wdt_pretimeout_info; + ret = clk_prepare_enable(wdev->clk); if (ret) return ret; diff --git a/drivers/watchdog/kempld_wdt.c b/drivers/watchdog/kempld_wdt.c index 5bf931ce1353..8e302d0e346c 100644 --- a/drivers/watchdog/kempld_wdt.c +++ b/drivers/watchdog/kempld_wdt.c @@ -430,7 +430,7 @@ static struct watchdog_info kempld_wdt_info = { WDIOF_PRETIMEOUT }; -static struct watchdog_ops kempld_wdt_ops = { +static const struct watchdog_ops kempld_wdt_ops = { .owner = THIS_MODULE, .start = kempld_wdt_start, .stop = kempld_wdt_stop, diff --git a/drivers/watchdog/mt7621_wdt.c b/drivers/watchdog/mt7621_wdt.c index 4a2290f900a8..d5735c12067d 100644 --- a/drivers/watchdog/mt7621_wdt.c +++ b/drivers/watchdog/mt7621_wdt.c @@ -139,7 +139,6 @@ static int mt7621_wdt_probe(struct platform_device *pdev) if (!IS_ERR(mt7621_wdt_reset)) reset_control_deassert(mt7621_wdt_reset); - mt7621_wdt_dev.dev = &pdev->dev; mt7621_wdt_dev.bootstatus = mt7621_wdt_bootcause(); watchdog_init_timeout(&mt7621_wdt_dev, mt7621_wdt_dev.max_timeout, diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c index b2e1b4cbbdc1..fae7fe929ea3 100644 --- a/drivers/watchdog/of_xilinx_wdt.c +++ b/drivers/watchdog/of_xilinx_wdt.c @@ -10,6 +10,7 @@ * 2 of the License, or (at your option) any later version. */ +#include <linux/clk.h> #include <linux/err.h> #include <linux/module.h> #include <linux/types.h> @@ -45,6 +46,7 @@ struct xwdt_device { u32 wdt_interval; spinlock_t spinlock; struct watchdog_device xilinx_wdt_wdd; + struct clk *clk; }; static int xilinx_wdt_start(struct watchdog_device *wdd) @@ -195,16 +197,30 @@ static int xwdt_probe(struct platform_device *pdev) spin_lock_init(&xdev->spinlock); watchdog_set_drvdata(xilinx_wdt_wdd, xdev); + xdev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(xdev->clk)) { + if (PTR_ERR(xdev->clk) == -ENOENT) + xdev->clk = NULL; + else + return PTR_ERR(xdev->clk); + } + + rc = clk_prepare_enable(xdev->clk); + if (rc) { + dev_err(&pdev->dev, "unable to enable clock\n"); + return rc; + } + rc = xwdt_selftest(xdev); if (rc == XWT_TIMER_FAILED) { dev_err(&pdev->dev, "SelfTest routine error\n"); - return rc; + goto err_clk_disable; } rc = watchdog_register_device(xilinx_wdt_wdd); if (rc) { dev_err(&pdev->dev, "Cannot register watchdog (err=%d)\n", rc); - return rc; + goto err_clk_disable; } dev_info(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds\n", @@ -213,6 +229,10 @@ static int xwdt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, xdev); return 0; +err_clk_disable: + clk_disable_unprepare(xdev->clk); + + return rc; } static int xwdt_remove(struct platform_device *pdev) @@ -220,6 +240,7 @@ static int xwdt_remove(struct platform_device *pdev) struct xwdt_device *xdev = platform_get_drvdata(pdev); watchdog_unregister_device(&xdev->xilinx_wdt_wdd); + clk_disable_unprepare(xdev->clk); return 0; } diff --git a/drivers/watchdog/pretimeout_noop.c b/drivers/watchdog/pretimeout_noop.c new file mode 100644 index 000000000000..85f5299d197c --- /dev/null +++ b/drivers/watchdog/pretimeout_noop.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015-2016 Mentor Graphics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/printk.h> +#include <linux/watchdog.h> + +#include "watchdog_pretimeout.h" + +/** + * pretimeout_noop - No operation on watchdog pretimeout event + * @wdd - watchdog_device + * + * This function prints a message about pretimeout to kernel log. + */ +static void pretimeout_noop(struct watchdog_device *wdd) +{ + pr_alert("watchdog%d: pretimeout event\n", wdd->id); +} + +static struct watchdog_governor watchdog_gov_noop = { + .name = "noop", + .pretimeout = pretimeout_noop, +}; + +static int __init watchdog_gov_noop_register(void) +{ + return watchdog_register_governor(&watchdog_gov_noop); +} + +static void __exit watchdog_gov_noop_unregister(void) +{ + watchdog_unregister_governor(&watchdog_gov_noop); +} +module_init(watchdog_gov_noop_register); +module_exit(watchdog_gov_noop_unregister); + +MODULE_AUTHOR("Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>"); +MODULE_DESCRIPTION("Panic watchdog pretimeout governor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/pretimeout_panic.c b/drivers/watchdog/pretimeout_panic.c new file mode 100644 index 000000000000..0c197a1c97f4 --- /dev/null +++ b/drivers/watchdog/pretimeout_panic.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015-2016 Mentor Graphics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/watchdog.h> + +#include "watchdog_pretimeout.h" + +/** + * pretimeout_panic - Panic on watchdog pretimeout event + * @wdd - watchdog_device + * + * Panic, watchdog has not been fed till pretimeout event. + */ +static void pretimeout_panic(struct watchdog_device *wdd) +{ + panic("watchdog pretimeout event\n"); +} + +static struct watchdog_governor watchdog_gov_panic = { + .name = "panic", + .pretimeout = pretimeout_panic, +}; + +static int __init watchdog_gov_panic_register(void) +{ + return watchdog_register_governor(&watchdog_gov_panic); +} + +static void __exit watchdog_gov_panic_unregister(void) +{ + watchdog_unregister_governor(&watchdog_gov_panic); +} +module_init(watchdog_gov_panic_register); +module_exit(watchdog_gov_panic_unregister); + +MODULE_AUTHOR("Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>"); +MODULE_DESCRIPTION("Panic watchdog pretimeout governor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/rn5t618_wdt.c b/drivers/watchdog/rn5t618_wdt.c index d1c12278cb6a..0805ee2acd7a 100644 --- a/drivers/watchdog/rn5t618_wdt.c +++ b/drivers/watchdog/rn5t618_wdt.c @@ -136,7 +136,7 @@ static struct watchdog_info rn5t618_wdt_info = { .identity = DRIVER_NAME, }; -static struct watchdog_ops rn5t618_wdt_ops = { +static const struct watchdog_ops rn5t618_wdt_ops = { .owner = THIS_MODULE, .start = rn5t618_wdt_start, .stop = rn5t618_wdt_stop, diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c index 1967919ae743..14b4fd428fff 100644 --- a/drivers/watchdog/rt2880_wdt.c +++ b/drivers/watchdog/rt2880_wdt.c @@ -158,7 +158,6 @@ static int rt288x_wdt_probe(struct platform_device *pdev) rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE; - rt288x_wdt_dev.dev = &pdev->dev; rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause(); rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq); rt288x_wdt_dev.parent = &pdev->dev; diff --git a/drivers/watchdog/softdog.c b/drivers/watchdog/softdog.c index b067edf246df..c7bdc986dca1 100644 --- a/drivers/watchdog/softdog.c +++ b/drivers/watchdog/softdog.c @@ -72,10 +72,27 @@ static void softdog_fire(unsigned long data) static struct timer_list softdog_ticktock = TIMER_INITIALIZER(softdog_fire, 0, 0); +static struct watchdog_device softdog_dev; + +static void softdog_pretimeout(unsigned long data) +{ + watchdog_notify_pretimeout(&softdog_dev); +} + +static struct timer_list softdog_preticktock = + TIMER_INITIALIZER(softdog_pretimeout, 0, 0); + static int softdog_ping(struct watchdog_device *w) { if (!mod_timer(&softdog_ticktock, jiffies + (w->timeout * HZ))) __module_get(THIS_MODULE); + + if (w->pretimeout) + mod_timer(&softdog_preticktock, jiffies + + (w->timeout - w->pretimeout) * HZ); + else + del_timer(&softdog_preticktock); + return 0; } @@ -84,15 +101,18 @@ static int softdog_stop(struct watchdog_device *w) if (del_timer(&softdog_ticktock)) module_put(THIS_MODULE); + del_timer(&softdog_preticktock); + return 0; } static struct watchdog_info softdog_info = { .identity = "Software Watchdog", - .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | + WDIOF_PRETIMEOUT, }; -static struct watchdog_ops softdog_ops = { +static const struct watchdog_ops softdog_ops = { .owner = THIS_MODULE, .start = softdog_ping, .stop = softdog_stop, diff --git a/drivers/watchdog/st_lpc_wdt.c b/drivers/watchdog/st_lpc_wdt.c index 14e9badf2bfa..e6100e447dd8 100644 --- a/drivers/watchdog/st_lpc_wdt.c +++ b/drivers/watchdog/st_lpc_wdt.c @@ -52,27 +52,6 @@ struct st_wdog { bool warm_reset; }; -static struct st_wdog_syscfg stid127_syscfg = { - .reset_type_reg = 0x004, - .reset_type_mask = BIT(2), - .enable_reg = 0x000, - .enable_mask = BIT(2), -}; - -static struct st_wdog_syscfg stih415_syscfg = { - .reset_type_reg = 0x0B8, - .reset_type_mask = BIT(6), - .enable_reg = 0x0B4, - .enable_mask = BIT(7), -}; - -static struct st_wdog_syscfg stih416_syscfg = { - .reset_type_reg = 0x88C, - .reset_type_mask = BIT(6), - .enable_reg = 0x888, - .enable_mask = BIT(7), -}; - static struct st_wdog_syscfg stih407_syscfg = { .enable_reg = 0x204, .enable_mask = BIT(19), @@ -83,18 +62,6 @@ static const struct of_device_id st_wdog_match[] = { .compatible = "st,stih407-lpc", .data = &stih407_syscfg, }, - { - .compatible = "st,stih416-lpc", - .data = &stih416_syscfg, - }, - { - .compatible = "st,stih415-lpc", - .data = &stih415_syscfg, - }, - { - .compatible = "st,stid127-lpc", - .data = &stid127_syscfg, - }, {}, }; MODULE_DEVICE_TABLE(of, st_wdog_match); diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c index 9ec57608da82..2d53c3f9394f 100644 --- a/drivers/watchdog/tegra_wdt.c +++ b/drivers/watchdog/tegra_wdt.c @@ -178,7 +178,7 @@ static const struct watchdog_info tegra_wdt_info = { .identity = "Tegra Watchdog", }; -static struct watchdog_ops tegra_wdt_ops = { +static const struct watchdog_ops tegra_wdt_ops = { .owner = THIS_MODULE, .start = tegra_wdt_start, .stop = tegra_wdt_stop, diff --git a/drivers/watchdog/txx9wdt.c b/drivers/watchdog/txx9wdt.c index c2da880292bc..6f7a9deb27d0 100644 --- a/drivers/watchdog/txx9wdt.c +++ b/drivers/watchdog/txx9wdt.c @@ -112,7 +112,7 @@ static int __init txx9wdt_probe(struct platform_device *dev) txx9_imclk = NULL; goto exit; } - ret = clk_enable(txx9_imclk); + ret = clk_prepare_enable(txx9_imclk); if (ret) { clk_put(txx9_imclk); txx9_imclk = NULL; @@ -144,7 +144,7 @@ static int __init txx9wdt_probe(struct platform_device *dev) return 0; exit: if (txx9_imclk) { - clk_disable(txx9_imclk); + clk_disable_unprepare(txx9_imclk); clk_put(txx9_imclk); } return ret; @@ -153,7 +153,7 @@ exit: static int __exit txx9wdt_remove(struct platform_device *dev) { watchdog_unregister_device(&txx9wdt); - clk_disable(txx9_imclk); + clk_disable_unprepare(txx9_imclk); clk_put(txx9_imclk); return 0; } diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index 09e8003039dc..ef2ecaf53a14 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -302,7 +302,7 @@ static struct watchdog_info wdt_info = { .identity = "W83627HF Watchdog", }; -static struct watchdog_ops wdt_ops = { +static const struct watchdog_ops wdt_ops = { .owner = THIS_MODULE, .start = wdt_start, .stop = wdt_stop, diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 6abb83cd7681..74265b2f806c 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -349,7 +349,7 @@ int devm_watchdog_register_device(struct device *dev, struct watchdog_device **rcwdd; int ret; - rcwdd = devres_alloc(devm_watchdog_unregister_device, sizeof(*wdd), + rcwdd = devres_alloc(devm_watchdog_unregister_device, sizeof(*rcwdd), GFP_KERNEL); if (!rcwdd) return -ENOMEM; diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 040bf8382f46..32930a073a12 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -49,6 +49,7 @@ #include <linux/uaccess.h> /* For copy_to_user/put_user/... */ #include "watchdog_core.h" +#include "watchdog_pretimeout.h" /* * struct watchdog_core_data - watchdog core internal data @@ -335,10 +336,14 @@ static int watchdog_set_timeout(struct watchdog_device *wdd, if (watchdog_timeout_invalid(wdd, timeout)) return -EINVAL; - if (wdd->ops->set_timeout) + if (wdd->ops->set_timeout) { err = wdd->ops->set_timeout(wdd, timeout); - else + } else { wdd->timeout = timeout; + /* Disable pretimeout if it doesn't fit the new timeout */ + if (wdd->pretimeout >= wdd->timeout) + wdd->pretimeout = 0; + } watchdog_update_worker(wdd); @@ -346,6 +351,31 @@ static int watchdog_set_timeout(struct watchdog_device *wdd, } /* + * watchdog_set_pretimeout: set the watchdog timer pretimeout + * @wdd: the watchdog device to set the timeout for + * @timeout: pretimeout to set in seconds + */ + +static int watchdog_set_pretimeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + int err = 0; + + if (!(wdd->info->options & WDIOF_PRETIMEOUT)) + return -EOPNOTSUPP; + + if (watchdog_pretimeout_invalid(wdd, timeout)) + return -EINVAL; + + if (wdd->ops->set_pretimeout) + err = wdd->ops->set_pretimeout(wdd, timeout); + else + wdd->pretimeout = timeout; + + return err; +} + +/* * watchdog_get_timeleft: wrapper to get the time left before a reboot * @wdd: the watchdog device to get the remaining time from * @timeleft: the time that's left @@ -429,6 +459,15 @@ static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(timeout); +static ssize_t pretimeout_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", wdd->pretimeout); +} +static DEVICE_ATTR_RO(pretimeout); + static ssize_t identity_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -450,6 +489,36 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(state); +static ssize_t pretimeout_available_governors_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return watchdog_pretimeout_available_governors_get(buf); +} +static DEVICE_ATTR_RO(pretimeout_available_governors); + +static ssize_t pretimeout_governor_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + + return watchdog_pretimeout_governor_get(wdd, buf); +} + +static ssize_t pretimeout_governor_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + int ret = watchdog_pretimeout_governor_set(wdd, buf); + + if (!ret) + ret = count; + + return ret; +} +static DEVICE_ATTR_RW(pretimeout_governor); + static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, int n) { @@ -459,6 +528,14 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft) mode = 0; + else if (attr == &dev_attr_pretimeout.attr && + !(wdd->info->options & WDIOF_PRETIMEOUT)) + mode = 0; + else if ((attr == &dev_attr_pretimeout_governor.attr || + attr == &dev_attr_pretimeout_available_governors.attr) && + (!(wdd->info->options & WDIOF_PRETIMEOUT) || + !IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV))) + mode = 0; return mode; } @@ -466,10 +543,13 @@ static struct attribute *wdt_attrs[] = { &dev_attr_state.attr, &dev_attr_identity.attr, &dev_attr_timeout.attr, + &dev_attr_pretimeout.attr, &dev_attr_timeleft.attr, &dev_attr_bootstatus.attr, &dev_attr_status.attr, &dev_attr_nowayout.attr, + &dev_attr_pretimeout_governor.attr, + &dev_attr_pretimeout_available_governors.attr, NULL, }; @@ -646,6 +726,16 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, break; err = put_user(val, p); break; + case WDIOC_SETPRETIMEOUT: + if (get_user(val, p)) { + err = -EFAULT; + break; + } + err = watchdog_set_pretimeout(wdd, val); + break; + case WDIOC_GETPRETIMEOUT: + err = put_user(wdd->pretimeout, p); + break; default: err = -ENOTTY; break; @@ -937,6 +1027,12 @@ int watchdog_dev_register(struct watchdog_device *wdd) return PTR_ERR(dev); } + ret = watchdog_register_pretimeout(wdd); + if (ret) { + device_destroy(&watchdog_class, devno); + watchdog_cdev_unregister(wdd); + } + return ret; } @@ -950,6 +1046,7 @@ int watchdog_dev_register(struct watchdog_device *wdd) void watchdog_dev_unregister(struct watchdog_device *wdd) { + watchdog_unregister_pretimeout(wdd); device_destroy(&watchdog_class, wdd->wd_data->cdev.dev); watchdog_cdev_unregister(wdd); } diff --git a/drivers/watchdog/watchdog_pretimeout.c b/drivers/watchdog/watchdog_pretimeout.c new file mode 100644 index 000000000000..9db07bfb4334 --- /dev/null +++ b/drivers/watchdog/watchdog_pretimeout.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2015-2016 Mentor Graphics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/watchdog.h> + +#include "watchdog_pretimeout.h" + +/* Default watchdog pretimeout governor */ +static struct watchdog_governor *default_gov; + +/* The spinlock protects default_gov, wdd->gov and pretimeout_list */ +static DEFINE_SPINLOCK(pretimeout_lock); + +/* List of watchdog devices, which can generate a pretimeout event */ +static LIST_HEAD(pretimeout_list); + +struct watchdog_pretimeout { + struct watchdog_device *wdd; + struct list_head entry; +}; + +/* The mutex protects governor list and serializes external interfaces */ +static DEFINE_MUTEX(governor_lock); + +/* List of the registered watchdog pretimeout governors */ +static LIST_HEAD(governor_list); + +struct governor_priv { + struct watchdog_governor *gov; + struct list_head entry; +}; + +static struct governor_priv *find_governor_by_name(const char *gov_name) +{ + struct governor_priv *priv; + + list_for_each_entry(priv, &governor_list, entry) + if (sysfs_streq(gov_name, priv->gov->name)) + return priv; + + return NULL; +} + +int watchdog_pretimeout_available_governors_get(char *buf) +{ + struct governor_priv *priv; + int count = 0; + + mutex_lock(&governor_lock); + + list_for_each_entry(priv, &governor_list, entry) + count += sprintf(buf + count, "%s\n", priv->gov->name); + + mutex_unlock(&governor_lock); + + return count; +} + +int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf) +{ + int count = 0; + + spin_lock_irq(&pretimeout_lock); + if (wdd->gov) + count = sprintf(buf, "%s\n", wdd->gov->name); + spin_unlock_irq(&pretimeout_lock); + + return count; +} + +int watchdog_pretimeout_governor_set(struct watchdog_device *wdd, + const char *buf) +{ + struct governor_priv *priv; + + mutex_lock(&governor_lock); + + priv = find_governor_by_name(buf); + if (!priv) { + mutex_unlock(&governor_lock); + return -EINVAL; + } + + spin_lock_irq(&pretimeout_lock); + wdd->gov = priv->gov; + spin_unlock_irq(&pretimeout_lock); + + mutex_unlock(&governor_lock); + + return 0; +} + +void watchdog_notify_pretimeout(struct watchdog_device *wdd) +{ + unsigned long flags; + + spin_lock_irqsave(&pretimeout_lock, flags); + if (!wdd->gov) { + spin_unlock_irqrestore(&pretimeout_lock, flags); + return; + } + + wdd->gov->pretimeout(wdd); + spin_unlock_irqrestore(&pretimeout_lock, flags); +} +EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout); + +int watchdog_register_governor(struct watchdog_governor *gov) +{ + struct watchdog_pretimeout *p; + struct governor_priv *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_lock(&governor_lock); + + if (find_governor_by_name(gov->name)) { + mutex_unlock(&governor_lock); + kfree(priv); + return -EBUSY; + } + + priv->gov = gov; + list_add(&priv->entry, &governor_list); + + if (!strncmp(gov->name, WATCHDOG_PRETIMEOUT_DEFAULT_GOV, + WATCHDOG_GOV_NAME_MAXLEN)) { + spin_lock_irq(&pretimeout_lock); + default_gov = gov; + + list_for_each_entry(p, &pretimeout_list, entry) + if (!p->wdd->gov) + p->wdd->gov = default_gov; + spin_unlock_irq(&pretimeout_lock); + } + + mutex_unlock(&governor_lock); + + return 0; +} +EXPORT_SYMBOL(watchdog_register_governor); + +void watchdog_unregister_governor(struct watchdog_governor *gov) +{ + struct watchdog_pretimeout *p; + struct governor_priv *priv, *t; + + mutex_lock(&governor_lock); + + list_for_each_entry_safe(priv, t, &governor_list, entry) { + if (priv->gov == gov) { + list_del(&priv->entry); + kfree(priv); + break; + } + } + + spin_lock_irq(&pretimeout_lock); + list_for_each_entry(p, &pretimeout_list, entry) + if (p->wdd->gov == gov) + p->wdd->gov = default_gov; + spin_unlock_irq(&pretimeout_lock); + + mutex_unlock(&governor_lock); +} +EXPORT_SYMBOL(watchdog_unregister_governor); + +int watchdog_register_pretimeout(struct watchdog_device *wdd) +{ + struct watchdog_pretimeout *p; + + if (!(wdd->info->options & WDIOF_PRETIMEOUT)) + return 0; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + + spin_lock_irq(&pretimeout_lock); + list_add(&p->entry, &pretimeout_list); + p->wdd = wdd; + wdd->gov = default_gov; + spin_unlock_irq(&pretimeout_lock); + + return 0; +} + +void watchdog_unregister_pretimeout(struct watchdog_device *wdd) +{ + struct watchdog_pretimeout *p, *t; + + if (!(wdd->info->options & WDIOF_PRETIMEOUT)) + return; + + spin_lock_irq(&pretimeout_lock); + wdd->gov = NULL; + + list_for_each_entry_safe(p, t, &pretimeout_list, entry) { + if (p->wdd == wdd) { + list_del(&p->entry); + break; + } + } + spin_unlock_irq(&pretimeout_lock); + + kfree(p); +} diff --git a/drivers/watchdog/watchdog_pretimeout.h b/drivers/watchdog/watchdog_pretimeout.h new file mode 100644 index 000000000000..a5a32b39c56d --- /dev/null +++ b/drivers/watchdog/watchdog_pretimeout.h @@ -0,0 +1,60 @@ +#ifndef __WATCHDOG_PRETIMEOUT_H +#define __WATCHDOG_PRETIMEOUT_H + +#define WATCHDOG_GOV_NAME_MAXLEN 20 + +struct watchdog_device; + +struct watchdog_governor { + const char name[WATCHDOG_GOV_NAME_MAXLEN]; + void (*pretimeout)(struct watchdog_device *wdd); +}; + +#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV) +/* Interfaces to watchdog pretimeout governors */ +int watchdog_register_governor(struct watchdog_governor *gov); +void watchdog_unregister_governor(struct watchdog_governor *gov); + +/* Interfaces to watchdog_dev.c */ +int watchdog_register_pretimeout(struct watchdog_device *wdd); +void watchdog_unregister_pretimeout(struct watchdog_device *wdd); +int watchdog_pretimeout_available_governors_get(char *buf); +int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf); +int watchdog_pretimeout_governor_set(struct watchdog_device *wdd, + const char *buf); + +#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP) +#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "noop" +#elif IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC) +#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV "panic" +#endif + +#else +static inline int watchdog_register_pretimeout(struct watchdog_device *wdd) +{ + return 0; +} + +static inline void watchdog_unregister_pretimeout(struct watchdog_device *wdd) +{ +} + +static inline int watchdog_pretimeout_available_governors_get(char *buf) +{ + return -EINVAL; +} + +static inline int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, + char *buf) +{ + return -EINVAL; +} + +static inline int watchdog_pretimeout_governor_set(struct watchdog_device *wdd, + const char *buf) +{ + return -EINVAL; +} +#endif + +#endif diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c index fa1efef3c96e..b4e0cea5a64e 100644 --- a/drivers/watchdog/ziirave_wdt.c +++ b/drivers/watchdog/ziirave_wdt.c @@ -18,7 +18,10 @@ * GNU General Public License for more details. */ +#include <linux/delay.h> #include <linux/i2c.h> +#include <linux/ihex.h> +#include <linux/firmware.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> @@ -36,6 +39,8 @@ #define ZIIRAVE_STATE_OFF 0x1 #define ZIIRAVE_STATE_ON 0x2 +#define ZIIRAVE_FW_NAME "ziirave_wdt.fw" + static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL, "host request", NULL, "illegal configuration", "illegal instruction", "illegal trap", @@ -50,12 +55,35 @@ static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL, #define ZIIRAVE_WDT_PING 0x9 #define ZIIRAVE_WDT_RESET_DURATION 0xa +#define ZIIRAVE_FIRM_PKT_TOTAL_SIZE 20 +#define ZIIRAVE_FIRM_PKT_DATA_SIZE 16 +#define ZIIRAVE_FIRM_FLASH_MEMORY_START 0x1600 +#define ZIIRAVE_FIRM_FLASH_MEMORY_END 0x2bbf + +/* Received and ready for next Download packet. */ +#define ZIIRAVE_FIRM_DOWNLOAD_ACK 1 +/* Currently writing to flash. Retry Download status in a moment! */ +#define ZIIRAVE_FIRM_DOWNLOAD_BUSY 2 + +/* Wait for ACK timeout in ms */ +#define ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT 50 + +/* Firmware commands */ +#define ZIIRAVE_CMD_DOWNLOAD_START 0x10 +#define ZIIRAVE_CMD_DOWNLOAD_END 0x11 +#define ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR 0x12 +#define ZIIRAVE_CMD_DOWNLOAD_READ_BYTE 0x13 +#define ZIIRAVE_CMD_RESET_PROCESSOR 0x0b +#define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER 0x0c +#define ZIIRAVE_CMD_DOWNLOAD_PACKET 0x0e + struct ziirave_wdt_rev { unsigned char major; unsigned char minor; }; struct ziirave_wdt_data { + struct mutex sysfs_mutex; struct watchdog_device wdd; struct ziirave_wdt_rev bootloader_rev; struct ziirave_wdt_rev firmware_rev; @@ -146,6 +174,293 @@ static unsigned int ziirave_wdt_get_timeleft(struct watchdog_device *wdd) return ret; } +static int ziirave_firm_wait_for_ack(struct watchdog_device *wdd) +{ + struct i2c_client *client = to_i2c_client(wdd->parent); + int ret; + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT); + do { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + + usleep_range(5000, 10000); + + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + dev_err(&client->dev, "Failed to read byte\n"); + return ret; + } + } while (ret == ZIIRAVE_FIRM_DOWNLOAD_BUSY); + + return ret == ZIIRAVE_FIRM_DOWNLOAD_ACK ? 0 : -EIO; +} + +static int ziirave_firm_set_read_addr(struct watchdog_device *wdd, u16 addr) +{ + struct i2c_client *client = to_i2c_client(wdd->parent); + u8 address[2]; + + address[0] = addr & 0xff; + address[1] = (addr >> 8) & 0xff; + + return i2c_smbus_write_block_data(client, + ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR, + ARRAY_SIZE(address), address); +} + +static int ziirave_firm_write_block_data(struct watchdog_device *wdd, + u8 command, u8 length, const u8 *data, + bool wait_for_ack) +{ + struct i2c_client *client = to_i2c_client(wdd->parent); + int ret; + + ret = i2c_smbus_write_block_data(client, command, length, data); + if (ret) { + dev_err(&client->dev, + "Failed to send command 0x%02x: %d\n", command, ret); + return ret; + } + + if (wait_for_ack) + ret = ziirave_firm_wait_for_ack(wdd); + + return ret; +} + +static int ziirave_firm_write_byte(struct watchdog_device *wdd, u8 command, + u8 byte, bool wait_for_ack) +{ + return ziirave_firm_write_block_data(wdd, command, 1, &byte, + wait_for_ack); +} + +/* + * ziirave_firm_write_pkt() - Build and write a firmware packet + * + * A packet to send to the firmware is composed by following bytes: + * Length | Addr0 | Addr1 | Data0 .. Data15 | Checksum | + * Where, + * Length: A data byte containing the length of the data. + * Addr0: Low byte of the address. + * Addr1: High byte of the address. + * Data0 .. Data15: Array of 16 bytes of data. + * Checksum: Checksum byte to verify data integrity. + */ +static int ziirave_firm_write_pkt(struct watchdog_device *wdd, + const struct ihex_binrec *rec) +{ + struct i2c_client *client = to_i2c_client(wdd->parent); + u8 i, checksum = 0, packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE]; + int ret; + u16 addr; + + memset(packet, 0, ARRAY_SIZE(packet)); + + /* Packet length */ + packet[0] = (u8)be16_to_cpu(rec->len); + /* Packet address */ + addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1; + packet[1] = addr & 0xff; + packet[2] = (addr & 0xff00) >> 8; + + /* Packet data */ + if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE) + return -EMSGSIZE; + memcpy(packet + 3, rec->data, be16_to_cpu(rec->len)); + + /* Packet checksum */ + for (i = 0; i < ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1; i++) + checksum += packet[i]; + packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1] = checksum; + + ret = ziirave_firm_write_block_data(wdd, ZIIRAVE_CMD_DOWNLOAD_PACKET, + ARRAY_SIZE(packet), packet, true); + if (ret) + dev_err(&client->dev, + "Failed to write firmware packet at address 0x%04x: %d\n", + addr, ret); + + return ret; +} + +static int ziirave_firm_verify(struct watchdog_device *wdd, + const struct firmware *fw) +{ + struct i2c_client *client = to_i2c_client(wdd->parent); + const struct ihex_binrec *rec; + int i, ret; + u8 data[ZIIRAVE_FIRM_PKT_DATA_SIZE]; + u16 addr; + + for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { + /* Zero length marks end of records */ + if (!be16_to_cpu(rec->len)) + break; + + addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1; + if (addr < ZIIRAVE_FIRM_FLASH_MEMORY_START || + addr > ZIIRAVE_FIRM_FLASH_MEMORY_END) + continue; + + ret = ziirave_firm_set_read_addr(wdd, addr); + if (ret) { + dev_err(&client->dev, + "Failed to send SET_READ_ADDR command: %d\n", + ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(data); i++) { + ret = i2c_smbus_read_byte_data(client, + ZIIRAVE_CMD_DOWNLOAD_READ_BYTE); + if (ret < 0) { + dev_err(&client->dev, + "Failed to READ DATA: %d\n", ret); + return ret; + } + data[i] = ret; + } + + if (memcmp(data, rec->data, be16_to_cpu(rec->len))) { + dev_err(&client->dev, + "Firmware mismatch at address 0x%04x\n", addr); + return -EINVAL; + } + } + + return 0; +} + +static int ziirave_firm_upload(struct watchdog_device *wdd, + const struct firmware *fw) +{ + struct i2c_client *client = to_i2c_client(wdd->parent); + int ret, words_till_page_break; + const struct ihex_binrec *rec; + struct ihex_binrec *rec_new; + + ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_JUMP_TO_BOOTLOADER, 1, + false); + if (ret) + return ret; + + msleep(500); + + ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_START, 1, true); + if (ret) + return ret; + + msleep(500); + + for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { + /* Zero length marks end of records */ + if (!be16_to_cpu(rec->len)) + break; + + /* Check max data size */ + if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE) { + dev_err(&client->dev, "Firmware packet too long (%d)\n", + be16_to_cpu(rec->len)); + return -EMSGSIZE; + } + + /* Calculate words till page break */ + words_till_page_break = (64 - ((be32_to_cpu(rec->addr) >> 1) & + 0x3f)); + if ((be16_to_cpu(rec->len) >> 1) > words_till_page_break) { + /* + * Data in passes page boundary, so we need to split in + * two blocks of data. Create a packet with the first + * block of data. + */ + rec_new = kzalloc(sizeof(struct ihex_binrec) + + (words_till_page_break << 1), + GFP_KERNEL); + if (!rec_new) + return -ENOMEM; + + rec_new->len = cpu_to_be16(words_till_page_break << 1); + rec_new->addr = rec->addr; + memcpy(rec_new->data, rec->data, + be16_to_cpu(rec_new->len)); + + ret = ziirave_firm_write_pkt(wdd, rec_new); + kfree(rec_new); + if (ret) + return ret; + + /* Create a packet with the second block of data */ + rec_new = kzalloc(sizeof(struct ihex_binrec) + + be16_to_cpu(rec->len) - + (words_till_page_break << 1), + GFP_KERNEL); + if (!rec_new) + return -ENOMEM; + + /* Remaining bytes */ + rec_new->len = rec->len - + cpu_to_be16(words_till_page_break << 1); + + rec_new->addr = cpu_to_be32(be32_to_cpu(rec->addr) + + (words_till_page_break << 1)); + + memcpy(rec_new->data, + rec->data + (words_till_page_break << 1), + be16_to_cpu(rec_new->len)); + + ret = ziirave_firm_write_pkt(wdd, rec_new); + kfree(rec_new); + if (ret) + return ret; + } else { + ret = ziirave_firm_write_pkt(wdd, rec); + if (ret) + return ret; + } + } + + /* For end of download, the length field will be set to 0 */ + rec_new = kzalloc(sizeof(struct ihex_binrec) + 1, GFP_KERNEL); + if (!rec_new) + return -ENOMEM; + + ret = ziirave_firm_write_pkt(wdd, rec_new); + kfree(rec_new); + if (ret) { + dev_err(&client->dev, "Failed to send EMPTY packet: %d\n", ret); + return ret; + } + + /* This sleep seems to be required */ + msleep(20); + + /* Start firmware verification */ + ret = ziirave_firm_verify(wdd, fw); + if (ret) { + dev_err(&client->dev, + "Failed to verify firmware: %d\n", ret); + return ret; + } + + /* End download operation */ + ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_END, 1, false); + if (ret) + return ret; + + /* Reset the processor */ + ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_RESET_PROCESSOR, 1, + false); + if (ret) + return ret; + + msleep(500); + + return 0; +} + static const struct watchdog_info ziirave_wdt_info = { .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .identity = "Zodiac RAVE Watchdog", @@ -166,9 +481,18 @@ static ssize_t ziirave_wdt_sysfs_show_firm(struct device *dev, { struct i2c_client *client = to_i2c_client(dev->parent); struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); + int ret; + + ret = mutex_lock_interruptible(&w_priv->sysfs_mutex); + if (ret) + return ret; + + ret = sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major, + w_priv->firmware_rev.minor); - return sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major, - w_priv->firmware_rev.minor); + mutex_unlock(&w_priv->sysfs_mutex); + + return ret; } static DEVICE_ATTR(firmware_version, S_IRUGO, ziirave_wdt_sysfs_show_firm, @@ -180,9 +504,18 @@ static ssize_t ziirave_wdt_sysfs_show_boot(struct device *dev, { struct i2c_client *client = to_i2c_client(dev->parent); struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); + int ret; - return sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major, - w_priv->bootloader_rev.minor); + ret = mutex_lock_interruptible(&w_priv->sysfs_mutex); + if (ret) + return ret; + + ret = sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major, + w_priv->bootloader_rev.minor); + + mutex_unlock(&w_priv->sysfs_mutex); + + return ret; } static DEVICE_ATTR(bootloader_version, S_IRUGO, ziirave_wdt_sysfs_show_boot, @@ -194,17 +527,81 @@ static ssize_t ziirave_wdt_sysfs_show_reason(struct device *dev, { struct i2c_client *client = to_i2c_client(dev->parent); struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); + int ret; + + ret = mutex_lock_interruptible(&w_priv->sysfs_mutex); + if (ret) + return ret; + + ret = sprintf(buf, "%s", ziirave_reasons[w_priv->reset_reason]); - return sprintf(buf, "%s", ziirave_reasons[w_priv->reset_reason]); + mutex_unlock(&w_priv->sysfs_mutex); + + return ret; } static DEVICE_ATTR(reset_reason, S_IRUGO, ziirave_wdt_sysfs_show_reason, NULL); +static ssize_t ziirave_wdt_sysfs_store_firm(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client); + const struct firmware *fw; + int err; + + err = request_ihex_firmware(&fw, ZIIRAVE_FW_NAME, dev); + if (err) { + dev_err(&client->dev, "Failed to request ihex firmware\n"); + return err; + } + + err = mutex_lock_interruptible(&w_priv->sysfs_mutex); + if (err) + goto release_firmware; + + err = ziirave_firm_upload(&w_priv->wdd, fw); + if (err) { + dev_err(&client->dev, "The firmware update failed: %d\n", err); + goto unlock_mutex; + } + + /* Update firmware version */ + err = ziirave_wdt_revision(client, &w_priv->firmware_rev, + ZIIRAVE_WDT_FIRM_VER_MAJOR); + if (err) { + dev_err(&client->dev, "Failed to read firmware version: %d\n", + err); + goto unlock_mutex; + } + + dev_info(&client->dev, "Firmware updated to version 02.%02u.%02u\n", + w_priv->firmware_rev.major, w_priv->firmware_rev.minor); + + /* Restore the watchdog timeout */ + err = ziirave_wdt_set_timeout(&w_priv->wdd, w_priv->wdd.timeout); + if (err) + dev_err(&client->dev, "Failed to set timeout: %d\n", err); + +unlock_mutex: + mutex_unlock(&w_priv->sysfs_mutex); + +release_firmware: + release_firmware(fw); + + return err ? err : count; +} + +static DEVICE_ATTR(update_firmware, S_IWUSR, NULL, + ziirave_wdt_sysfs_store_firm); + static struct attribute *ziirave_wdt_attrs[] = { &dev_attr_firmware_version.attr, &dev_attr_bootloader_version.attr, &dev_attr_reset_reason.attr, + &dev_attr_update_firmware.attr, NULL }; ATTRIBUTE_GROUPS(ziirave_wdt); @@ -252,6 +649,8 @@ static int ziirave_wdt_probe(struct i2c_client *client, if (!w_priv) return -ENOMEM; + mutex_init(&w_priv->sysfs_mutex); + w_priv->wdd.info = &ziirave_wdt_info; w_priv->wdd.ops = &ziirave_wdt_ops; w_priv->wdd.min_timeout = ZIIRAVE_TIMEOUT_MIN; |