diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/fpga/xilinx-pr-decoupler.c | 2 | ||||
-rw-r--r-- | drivers/fsi/fsi-core.c | 6 | ||||
-rw-r--r-- | drivers/hv/channel_mgmt.c | 10 | ||||
-rw-r--r-- | drivers/hv/hyperv_vmbus.h | 2 | ||||
-rw-r--r-- | drivers/hv/vmbus_drv.c | 185 | ||||
-rw-r--r-- | drivers/misc/altera-stapl/Kconfig | 3 | ||||
-rw-r--r-- | drivers/misc/mic/Kconfig | 4 | ||||
-rw-r--r-- | drivers/misc/pci_endpoint_test.c | 11 | ||||
-rw-r--r-- | drivers/nvmem/Kconfig | 10 | ||||
-rw-r--r-- | drivers/nvmem/Makefile | 2 | ||||
-rw-r--r-- | drivers/nvmem/core.c | 11 | ||||
-rw-r--r-- | drivers/nvmem/snvs_lpgpr.c | 156 | ||||
-rw-r--r-- | drivers/pcmcia/cistpl.c | 2 | ||||
-rw-r--r-- | drivers/pcmcia/cs_internal.h | 2 | ||||
-rw-r--r-- | drivers/vme/vme.c | 18 | ||||
-rw-r--r-- | drivers/w1/slaves/Kconfig | 15 | ||||
-rw-r--r-- | drivers/w1/slaves/Makefile | 1 | ||||
-rw-r--r-- | drivers/w1/slaves/w1_ds28e17.c | 771 | ||||
-rw-r--r-- | drivers/w1/w1_io.c | 3 |
19 files changed, 1177 insertions, 37 deletions
diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c index e359930bebc8..0d7743089414 100644 --- a/drivers/fpga/xilinx-pr-decoupler.c +++ b/drivers/fpga/xilinx-pr-decoupler.c @@ -79,7 +79,7 @@ static int xlnx_pr_decoupler_enable_show(struct fpga_bridge *bridge) return !status; } -static struct fpga_bridge_ops xlnx_pr_decoupler_br_ops = { +static const struct fpga_bridge_ops xlnx_pr_decoupler_br_ops = { .enable_set = xlnx_pr_decoupler_enable_set, .enable_show = xlnx_pr_decoupler_enable_show, }; diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index 4ea63d9bd131..e318bf8c623c 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -185,7 +185,7 @@ static int fsi_slave_calc_addr(struct fsi_slave *slave, uint32_t *addrp, return 0; } -int fsi_slave_report_and_clear_errors(struct fsi_slave *slave) +static int fsi_slave_report_and_clear_errors(struct fsi_slave *slave) { struct fsi_master *master = slave->master; uint32_t irq, stat; @@ -215,8 +215,8 @@ int fsi_slave_report_and_clear_errors(struct fsi_slave *slave) static int fsi_slave_set_smode(struct fsi_master *master, int link, int id); -int fsi_slave_handle_error(struct fsi_slave *slave, bool write, uint32_t addr, - size_t size) +static int fsi_slave_handle_error(struct fsi_slave *slave, bool write, + uint32_t addr, size_t size) { struct fsi_master *master = slave->master; int rc, link; diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 018d2e0f8ec5..c942a1120cb9 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -350,7 +350,7 @@ static void free_channel(struct vmbus_channel *channel) { tasklet_kill(&channel->callback_event); - kfree_rcu(channel, rcu); + kobject_put(&channel->kobj); } static void percpu_channel_enq(void *arg) @@ -520,6 +520,14 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) newchannel->state = CHANNEL_OPEN_STATE; if (!fnew) { + struct hv_device *dev + = newchannel->primary_channel->device_obj; + + if (vmbus_add_channel_kobj(dev, newchannel)) { + atomic_dec(&vmbus_connection.offer_in_progress); + goto err_free_chan; + } + if (channel->sc_creation_callback != NULL) channel->sc_creation_callback(newchannel); newchannel->probe_done = true; diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 49569f8fe038..de6f01df9592 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -373,6 +373,8 @@ struct hv_device *vmbus_device_create(const uuid_le *type, int vmbus_device_register(struct hv_device *child_device_obj); void vmbus_device_unregister(struct hv_device *device_obj); +int vmbus_add_channel_kobj(struct hv_device *device_obj, + struct vmbus_channel *channel); struct vmbus_channel *relid2channel(u32 relid); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 937801ac2fe0..1da8e818f4de 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -107,28 +107,30 @@ static void print_alias_name(struct hv_device *hv_dev, char *alias_name) sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]); } -static u8 channel_monitor_group(struct vmbus_channel *channel) +static u8 channel_monitor_group(const struct vmbus_channel *channel) { return (u8)channel->offermsg.monitorid / 32; } -static u8 channel_monitor_offset(struct vmbus_channel *channel) +static u8 channel_monitor_offset(const struct vmbus_channel *channel) { return (u8)channel->offermsg.monitorid % 32; } -static u32 channel_pending(struct vmbus_channel *channel, - struct hv_monitor_page *monitor_page) +static u32 channel_pending(const struct vmbus_channel *channel, + const struct hv_monitor_page *monitor_page) { u8 monitor_group = channel_monitor_group(channel); + return monitor_page->trigger_group[monitor_group].pending; } -static u32 channel_latency(struct vmbus_channel *channel, - struct hv_monitor_page *monitor_page) +static u32 channel_latency(const struct vmbus_channel *channel, + const struct hv_monitor_page *monitor_page) { u8 monitor_group = channel_monitor_group(channel); u8 monitor_offset = channel_monitor_offset(channel); + return monitor_page->latency[monitor_group][monitor_offset]; } @@ -1133,6 +1135,145 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver) } EXPORT_SYMBOL_GPL(vmbus_driver_unregister); + +/* + * Called when last reference to channel is gone. + */ +static void vmbus_chan_release(struct kobject *kobj) +{ + struct vmbus_channel *channel + = container_of(kobj, struct vmbus_channel, kobj); + + kfree_rcu(channel, rcu); +} + +struct vmbus_chan_attribute { + struct attribute attr; + ssize_t (*show)(const struct vmbus_channel *chan, char *buf); + ssize_t (*store)(struct vmbus_channel *chan, + const char *buf, size_t count); +}; +#define VMBUS_CHAN_ATTR(_name, _mode, _show, _store) \ + struct vmbus_chan_attribute chan_attr_##_name \ + = __ATTR(_name, _mode, _show, _store) +#define VMBUS_CHAN_ATTR_RW(_name) \ + struct vmbus_chan_attribute chan_attr_##_name = __ATTR_RW(_name) +#define VMBUS_CHAN_ATTR_RO(_name) \ + struct vmbus_chan_attribute chan_attr_##_name = __ATTR_RO(_name) +#define VMBUS_CHAN_ATTR_WO(_name) \ + struct vmbus_chan_attribute chan_attr_##_name = __ATTR_WO(_name) + +static ssize_t vmbus_chan_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + const struct vmbus_chan_attribute *attribute + = container_of(attr, struct vmbus_chan_attribute, attr); + const struct vmbus_channel *chan + = container_of(kobj, struct vmbus_channel, kobj); + + if (!attribute->show) + return -EIO; + + return attribute->show(chan, buf); +} + +static const struct sysfs_ops vmbus_chan_sysfs_ops = { + .show = vmbus_chan_attr_show, +}; + +static ssize_t out_mask_show(const struct vmbus_channel *channel, char *buf) +{ + const struct hv_ring_buffer_info *rbi = &channel->outbound; + + return sprintf(buf, "%u\n", rbi->ring_buffer->interrupt_mask); +} +VMBUS_CHAN_ATTR_RO(out_mask); + +static ssize_t in_mask_show(const struct vmbus_channel *channel, char *buf) +{ + const struct hv_ring_buffer_info *rbi = &channel->inbound; + + return sprintf(buf, "%u\n", rbi->ring_buffer->interrupt_mask); +} +VMBUS_CHAN_ATTR_RO(in_mask); + +static ssize_t read_avail_show(const struct vmbus_channel *channel, char *buf) +{ + const struct hv_ring_buffer_info *rbi = &channel->inbound; + + return sprintf(buf, "%u\n", hv_get_bytes_to_read(rbi)); +} +VMBUS_CHAN_ATTR_RO(read_avail); + +static ssize_t write_avail_show(const struct vmbus_channel *channel, char *buf) +{ + const struct hv_ring_buffer_info *rbi = &channel->outbound; + + return sprintf(buf, "%u\n", hv_get_bytes_to_write(rbi)); +} +VMBUS_CHAN_ATTR_RO(write_avail); + +static ssize_t show_target_cpu(const struct vmbus_channel *channel, char *buf) +{ + return sprintf(buf, "%u\n", channel->target_cpu); +} +VMBUS_CHAN_ATTR(cpu, S_IRUGO, show_target_cpu, NULL); + +static ssize_t channel_pending_show(const struct vmbus_channel *channel, + char *buf) +{ + return sprintf(buf, "%d\n", + channel_pending(channel, + vmbus_connection.monitor_pages[1])); +} +VMBUS_CHAN_ATTR(pending, S_IRUGO, channel_pending_show, NULL); + +static ssize_t channel_latency_show(const struct vmbus_channel *channel, + char *buf) +{ + return sprintf(buf, "%d\n", + channel_latency(channel, + vmbus_connection.monitor_pages[1])); +} +VMBUS_CHAN_ATTR(latency, S_IRUGO, channel_latency_show, NULL); + +static struct attribute *vmbus_chan_attrs[] = { + &chan_attr_out_mask.attr, + &chan_attr_in_mask.attr, + &chan_attr_read_avail.attr, + &chan_attr_write_avail.attr, + &chan_attr_cpu.attr, + &chan_attr_pending.attr, + &chan_attr_latency.attr, + NULL +}; + +static struct kobj_type vmbus_chan_ktype = { + .sysfs_ops = &vmbus_chan_sysfs_ops, + .release = vmbus_chan_release, + .default_attrs = vmbus_chan_attrs, +}; + +/* + * vmbus_add_channel_kobj - setup a sub-directory under device/channels + */ +int vmbus_add_channel_kobj(struct hv_device *dev, struct vmbus_channel *channel) +{ + struct kobject *kobj = &channel->kobj; + u32 relid = channel->offermsg.child_relid; + int ret; + + kobj->kset = dev->channels_kset; + ret = kobject_init_and_add(kobj, &vmbus_chan_ktype, NULL, + "%u", relid); + if (ret) + return ret; + + kobject_uevent(kobj, KOBJ_ADD); + + return 0; +} + /* * vmbus_device_create - Creates and registers a new child device * on the vmbus. @@ -1164,7 +1305,8 @@ struct hv_device *vmbus_device_create(const uuid_le *type, */ int vmbus_device_register(struct hv_device *child_device_obj) { - int ret = 0; + struct kobject *kobj = &child_device_obj->device.kobj; + int ret; dev_set_name(&child_device_obj->device, "%pUl", child_device_obj->channel->offermsg.offer.if_instance.b); @@ -1178,13 +1320,32 @@ int vmbus_device_register(struct hv_device *child_device_obj) * binding...which will eventually call vmbus_match() and vmbus_probe() */ ret = device_register(&child_device_obj->device); - - if (ret) + if (ret) { pr_err("Unable to register child device\n"); - else - pr_debug("child device %s registered\n", - dev_name(&child_device_obj->device)); + return ret; + } + + child_device_obj->channels_kset = kset_create_and_add("channels", + NULL, kobj); + if (!child_device_obj->channels_kset) { + ret = -ENOMEM; + goto err_dev_unregister; + } + + ret = vmbus_add_channel_kobj(child_device_obj, + child_device_obj->channel); + if (ret) { + pr_err("Unable to register primary channeln"); + goto err_kset_unregister; + } + + return 0; + +err_kset_unregister: + kset_unregister(child_device_obj->channels_kset); +err_dev_unregister: + device_unregister(&child_device_obj->device); return ret; } diff --git a/drivers/misc/altera-stapl/Kconfig b/drivers/misc/altera-stapl/Kconfig index 7f01d8e93992..8a828fe41fad 100644 --- a/drivers/misc/altera-stapl/Kconfig +++ b/drivers/misc/altera-stapl/Kconfig @@ -1,4 +1,5 @@ -comment "Altera FPGA firmware download module" +comment "Altera FPGA firmware download module (requires I2C)" + depends on !I2C config ALTERA_STAPL tristate "Altera FPGA firmware download module" diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index 6fd9d367dea7..227cc7443671 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -1,3 +1,5 @@ +menu "Intel MIC & related support" + comment "Intel MIC Bus Driver" config INTEL_MIC_BUS @@ -150,3 +152,5 @@ config VOP if VOP source "drivers/vhost/Kconfig.vringh" endif + +endmenu diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index deb203026496..d40a34f594c7 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -226,6 +226,9 @@ static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size) u32 src_crc32; u32 dst_crc32; + if (size > SIZE_MAX - alignment) + goto err; + orig_src_addr = dma_alloc_coherent(dev, size + alignment, &orig_src_phys_addr, GFP_KERNEL); if (!orig_src_addr) { @@ -311,6 +314,9 @@ static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size) size_t alignment = test->alignment; u32 crc32; + if (size > SIZE_MAX - alignment) + goto err; + orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr, GFP_KERNEL); if (!orig_addr) { @@ -369,6 +375,9 @@ static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size) size_t alignment = test->alignment; u32 crc32; + if (size > SIZE_MAX - alignment) + goto err; + orig_addr = dma_alloc_coherent(dev, size + alignment, &orig_phys_addr, GFP_KERNEL); if (!orig_addr) { @@ -588,6 +597,8 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) if (sscanf(misc_device->name, DRV_MODULE_NAME ".%d", &id) != 1) return; + if (id < 0) + return; misc_deregister(&test->miscdev); ida_simple_remove(&pci_endpoint_test_ida, id); diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 101ced4c84be..ea3044c5d6ee 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -144,4 +144,14 @@ config MESON_EFUSE This driver can also be built as a module. If so, the module will be called nvmem_meson_efuse. +config NVMEM_SNVS_LPGPR + tristate "Support for Low Power General Purpose Register" + depends on SOC_IMX6 || COMPILE_TEST + help + This is a driver for Low Power General Purpose Register (LPGPR) available on + i.MX6 SoCs in Secure Non-Volatile Storage (SNVS) of this chip. + + This driver can also be built as a module. If so, the module + will be called nvmem-snvs-lpgpr. + endif diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile index 173140658693..4c589184acee 100644 --- a/drivers/nvmem/Makefile +++ b/drivers/nvmem/Makefile @@ -30,3 +30,5 @@ obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o nvmem-vf610-ocotp-y := vf610-ocotp.o obj-$(CONFIG_MESON_EFUSE) += nvmem_meson_efuse.o nvmem_meson_efuse-y := meson-efuse.o +obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o +nvmem_snvs_lpgpr-y := snvs_lpgpr.o diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index d12e5de78e70..864904594063 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -615,7 +615,7 @@ static struct nvmem_device *nvmem_find(const char *name) return to_nvmem_device(d); } -#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) +#if IS_ENABLED(CONFIG_OF) /** * of_nvmem_device_get() - Get nvmem device from a given id * @@ -753,7 +753,7 @@ static struct nvmem_cell *nvmem_cell_get_from_list(const char *cell_id) return cell; } -#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) +#if IS_ENABLED(CONFIG_OF) /** * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id * @@ -946,8 +946,7 @@ void nvmem_cell_put(struct nvmem_cell *cell) } EXPORT_SYMBOL_GPL(nvmem_cell_put); -static inline void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, - void *buf) +static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf) { u8 *p, *b; int i, bit_offset = cell->bit_offset; @@ -1028,8 +1027,8 @@ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len) } EXPORT_SYMBOL_GPL(nvmem_cell_read); -static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell, - u8 *_buf, int len) +static void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell, + u8 *_buf, int len) { struct nvmem_device *nvmem = cell->nvmem; int i, rc, nbits, bit_offset = cell->bit_offset; diff --git a/drivers/nvmem/snvs_lpgpr.c b/drivers/nvmem/snvs_lpgpr.c new file mode 100644 index 000000000000..e5c2a4a17f03 --- /dev/null +++ b/drivers/nvmem/snvs_lpgpr.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de> + * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> + * + * 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/mfd/syscon.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> +#include <linux/of_device.h> +#include <linux/regmap.h> + +#define IMX6Q_SNVS_HPLR 0x00 +#define IMX6Q_GPR_SL BIT(5) +#define IMX6Q_SNVS_LPLR 0x34 +#define IMX6Q_GPR_HL BIT(5) +#define IMX6Q_SNVS_LPGPR 0x68 + +struct snvs_lpgpr_cfg { + int offset; + int offset_hplr; + int offset_lplr; +}; + +struct snvs_lpgpr_priv { + struct device_d *dev; + struct regmap *regmap; + struct nvmem_config cfg; + const struct snvs_lpgpr_cfg *dcfg; +}; + +static const struct snvs_lpgpr_cfg snvs_lpgpr_cfg_imx6q = { + .offset = IMX6Q_SNVS_LPGPR, + .offset_hplr = IMX6Q_SNVS_HPLR, + .offset_lplr = IMX6Q_SNVS_LPLR, +}; + +static int snvs_lpgpr_write(void *context, unsigned int offset, void *val, + size_t bytes) +{ + struct snvs_lpgpr_priv *priv = context; + const struct snvs_lpgpr_cfg *dcfg = priv->dcfg; + unsigned int lock_reg; + int ret; + + ret = regmap_read(priv->regmap, dcfg->offset_hplr, &lock_reg); + if (ret < 0) + return ret; + + if (lock_reg & IMX6Q_GPR_SL) + return -EPERM; + + ret = regmap_read(priv->regmap, dcfg->offset_lplr, &lock_reg); + if (ret < 0) + return ret; + + if (lock_reg & IMX6Q_GPR_HL) + return -EPERM; + + return regmap_bulk_write(priv->regmap, dcfg->offset + offset, val, + bytes / 4); +} + +static int snvs_lpgpr_read(void *context, unsigned int offset, void *val, + size_t bytes) +{ + struct snvs_lpgpr_priv *priv = context; + const struct snvs_lpgpr_cfg *dcfg = priv->dcfg; + + return regmap_bulk_read(priv->regmap, dcfg->offset + offset, + val, bytes / 4); +} + +static int snvs_lpgpr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct device_node *syscon_node; + struct snvs_lpgpr_priv *priv; + struct nvmem_config *cfg; + struct nvmem_device *nvmem; + const struct snvs_lpgpr_cfg *dcfg; + + if (!node) + return -ENOENT; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dcfg = of_device_get_match_data(dev); + if (!dcfg) + return -EINVAL; + + syscon_node = of_get_parent(node); + if (!syscon_node) + return -ENODEV; + + priv->regmap = syscon_node_to_regmap(syscon_node); + of_node_put(syscon_node); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->dcfg = dcfg; + + cfg = &priv->cfg; + cfg->priv = priv; + cfg->name = dev_name(dev); + cfg->dev = dev; + cfg->stride = 4, + cfg->word_size = 4, + cfg->size = 4, + cfg->owner = THIS_MODULE, + cfg->reg_read = snvs_lpgpr_read, + cfg->reg_write = snvs_lpgpr_write, + + nvmem = nvmem_register(cfg); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + platform_set_drvdata(pdev, nvmem); + + return 0; +} + +static int snvs_lpgpr_remove(struct platform_device *pdev) +{ + struct nvmem_device *nvmem = platform_get_drvdata(pdev); + + return nvmem_unregister(nvmem); +} + +static const struct of_device_id snvs_lpgpr_dt_ids[] = { + { .compatible = "fsl,imx6q-snvs-lpgpr", .data = &snvs_lpgpr_cfg_imx6q }, + { .compatible = "fsl,imx6ul-snvs-lpgpr", + .data = &snvs_lpgpr_cfg_imx6q }, + { }, +}; +MODULE_DEVICE_TABLE(of, snvs_lpgpr_dt_ids); + +static struct platform_driver snvs_lpgpr_driver = { + .probe = snvs_lpgpr_probe, + .remove = snvs_lpgpr_remove, + .driver = { + .name = "snvs_lpgpr", + .of_match_table = snvs_lpgpr_dt_ids, + }, +}; +module_platform_driver(snvs_lpgpr_driver); + +MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>"); +MODULE_DESCRIPTION("Low Power General Purpose Register in i.MX6 Secure Non-Volatile Storage"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 55ef7d1fd8da..102646fedb56 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -1599,7 +1599,7 @@ static ssize_t pccard_store_cis(struct file *filp, struct kobject *kobj, } -struct bin_attribute pccard_cis_attr = { +const struct bin_attribute pccard_cis_attr = { .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR }, .size = 0x200, .read = pccard_show_cis, diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index e86cd6b31773..6765beadea95 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -152,7 +152,7 @@ void pcmcia_cleanup_irq(struct pcmcia_socket *s); int pcmcia_setup_irq(struct pcmcia_device *p_dev); /* cistpl.c */ -extern struct bin_attribute pccard_cis_attr; +extern const struct bin_attribute pccard_cis_attr; int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr); diff --git a/drivers/vme/vme.c b/drivers/vme/vme.c index 6a3ead42aba8..5b4c898d7509 100644 --- a/drivers/vme/vme.c +++ b/drivers/vme/vme.c @@ -208,29 +208,27 @@ int vme_check_window(u32 aspace, unsigned long long vme_base, { int retval = 0; + if (vme_base + size < size) + return -EINVAL; + switch (aspace) { case VME_A16: - if (((vme_base + size) > VME_A16_MAX) || - (vme_base > VME_A16_MAX)) + if (vme_base + size > VME_A16_MAX) retval = -EFAULT; break; case VME_A24: - if (((vme_base + size) > VME_A24_MAX) || - (vme_base > VME_A24_MAX)) + if (vme_base + size > VME_A24_MAX) retval = -EFAULT; break; case VME_A32: - if (((vme_base + size) > VME_A32_MAX) || - (vme_base > VME_A32_MAX)) + if (vme_base + size > VME_A32_MAX) retval = -EFAULT; break; case VME_A64: - if ((size != 0) && (vme_base > U64_MAX + 1 - size)) - retval = -EFAULT; + /* The VME_A64_MAX limit is actually U64_MAX + 1 */ break; case VME_CRCSR: - if (((vme_base + size) > VME_CRCSR_MAX) || - (vme_base > VME_CRCSR_MAX)) + if (vme_base + size > VME_CRCSR_MAX) retval = -EFAULT; break; case VME_USER1: diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 3c945f9f5f0f..7931231d8e80 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -148,4 +148,19 @@ config W1_SLAVE_DS28E04 If you are unsure, say N. +config W1_SLAVE_DS28E17 + tristate "1-wire-to-I2C master bridge (DS28E17)" + select CRC16 + depends on I2C + help + Say Y here if you want to use the DS28E17 1-wire-to-I2C master bridge. + For each DS28E17 detected, a new I2C adapter is created within the + kernel. I2C devices on that bus can be configured to be used by the + kernel and userspace tools as on any other "native" I2C bus. + + This driver is also available as a module. If so, the module + will be called w1_ds28e17. + + If you are unsure, say N. + endmenu diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index 36b22fb2d3a1..855371a99e6a 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o obj-$(CONFIG_W1_SLAVE_DS2780) += w1_ds2780.o obj-$(CONFIG_W1_SLAVE_DS2781) += w1_ds2781.o obj-$(CONFIG_W1_SLAVE_DS28E04) += w1_ds28e04.o +obj-$(CONFIG_W1_SLAVE_DS28E17) += w1_ds28e17.o diff --git a/drivers/w1/slaves/w1_ds28e17.c b/drivers/w1/slaves/w1_ds28e17.c new file mode 100644 index 000000000000..e78b63ea4daf --- /dev/null +++ b/drivers/w1/slaves/w1_ds28e17.c @@ -0,0 +1,771 @@ +/* + * w1_ds28e17.c - w1 family 19 (DS28E17) driver + * + * Copyright (c) 2016 Jan Kandziora <jjj@gmx.de> + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <linux/crc16.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/uaccess.h> + +#define CRC16_INIT 0 + +#include <linux/w1.h> + +#define W1_FAMILY_DS28E17 0x19 + +/* Module setup. */ +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Jan Kandziora <jjj@gmx.de>"); +MODULE_DESCRIPTION("w1 family 19 driver for DS28E17, 1-wire to I2C master bridge"); +MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_DS28E17)); + + +/* Default I2C speed to be set when a DS28E17 is detected. */ +static int i2c_speed = 100; +module_param_named(speed, i2c_speed, int, (S_IRUSR | S_IWUSR)); +MODULE_PARM_DESC(speed, "Default I2C speed to be set when a DS28E17 is detected"); + +/* Default I2C stretch value to be set when a DS28E17 is detected. */ +static char i2c_stretch = 1; +module_param_named(stretch, i2c_stretch, byte, (S_IRUSR | S_IWUSR)); +MODULE_PARM_DESC(stretch, "Default I2C stretch value to be set when a DS28E17 is detected"); + +/* DS28E17 device command codes. */ +#define W1_F19_WRITE_DATA_WITH_STOP 0x4B +#define W1_F19_WRITE_DATA_NO_STOP 0x5A +#define W1_F19_WRITE_DATA_ONLY 0x69 +#define W1_F19_WRITE_DATA_ONLY_WITH_STOP 0x78 +#define W1_F19_READ_DATA_WITH_STOP 0x87 +#define W1_F19_WRITE_READ_DATA_WITH_STOP 0x2D +#define W1_F19_WRITE_CONFIGURATION 0xD2 +#define W1_F19_READ_CONFIGURATION 0xE1 +#define W1_F19_ENABLE_SLEEP_MODE 0x1E +#define W1_F19_READ_DEVICE_REVISION 0xC4 + +/* DS28E17 status bits */ +#define W1_F19_STATUS_CRC 0x01 +#define W1_F19_STATUS_ADDRESS 0x02 +#define W1_F19_STATUS_START 0x08 + +/* + * Maximum number of I2C bytes to transfer within one CRC16 protected onewire + * command. + * */ +#define W1_F19_WRITE_DATA_LIMIT 255 + +/* Maximum number of I2C bytes to read with one onewire command. */ +#define W1_F19_READ_DATA_LIMIT 255 + +/* Constants for calculating the busy sleep. */ +#define W1_F19_BUSY_TIMEBASES { 90, 23, 10 } +#define W1_F19_BUSY_GRATUITY 1000 + +/* Number of checks for the busy flag before timeout. */ +#define W1_F19_BUSY_CHECKS 1000 + + +/* Slave specific data. */ +struct w1_f19_data { + u8 speed; + u8 stretch; + struct i2c_adapter adapter; +}; + + +/* Wait a while until the busy flag clears. */ +static int w1_f19_i2c_busy_wait(struct w1_slave *sl, size_t count) +{ + const unsigned long timebases[3] = W1_F19_BUSY_TIMEBASES; + struct w1_f19_data *data = sl->family_data; + unsigned int checks; + + /* Check the busy flag first in any case.*/ + if (w1_touch_bit(sl->master, 1) == 0) + return 0; + + /* + * Do a generously long sleep in the beginning, + * as we have to wait at least this time for all + * the I2C bytes at the given speed to be transferred. + */ + usleep_range(timebases[data->speed] * (data->stretch) * count, + timebases[data->speed] * (data->stretch) * count + + W1_F19_BUSY_GRATUITY); + + /* Now continusly check the busy flag sent by the DS28E17. */ + checks = W1_F19_BUSY_CHECKS; + while ((checks--) > 0) { + /* Return success if the busy flag is cleared. */ + if (w1_touch_bit(sl->master, 1) == 0) + return 0; + + /* Wait one non-streched byte timeslot. */ + udelay(timebases[data->speed]); + } + + /* Timeout. */ + dev_warn(&sl->dev, "busy timeout\n"); + return -ETIMEDOUT; +} + + +/* Utility function: result. */ +static size_t w1_f19_error(struct w1_slave *sl, u8 w1_buf[]) +{ + /* Warnings. */ + if (w1_buf[0] & W1_F19_STATUS_CRC) + dev_warn(&sl->dev, "crc16 mismatch\n"); + if (w1_buf[0] & W1_F19_STATUS_ADDRESS) + dev_warn(&sl->dev, "i2c device not responding\n"); + if ((w1_buf[0] & (W1_F19_STATUS_CRC | W1_F19_STATUS_ADDRESS)) == 0 + && w1_buf[1] != 0) { + dev_warn(&sl->dev, "i2c short write, %d bytes not acknowledged\n", + w1_buf[1]); + } + + /* Check error conditions. */ + if (w1_buf[0] & W1_F19_STATUS_ADDRESS) + return -ENXIO; + if (w1_buf[0] & W1_F19_STATUS_START) + return -EAGAIN; + if (w1_buf[0] != 0 || w1_buf[1] != 0) + return -EIO; + + /* All ok. */ + return 0; +} + + +/* Utility function: write data to I2C slave, single chunk. */ +static int __w1_f19_i2c_write(struct w1_slave *sl, + const u8 *command, size_t command_count, + const u8 *buffer, size_t count) +{ + u16 crc; + int error; + u8 w1_buf[2]; + + /* Send command and I2C data to DS28E17. */ + crc = crc16(CRC16_INIT, command, command_count); + w1_write_block(sl->master, command, command_count); + + w1_buf[0] = count; + crc = crc16(crc, w1_buf, 1); + w1_write_8(sl->master, w1_buf[0]); + + crc = crc16(crc, buffer, count); + w1_write_block(sl->master, buffer, count); + + w1_buf[0] = ~(crc & 0xFF); + w1_buf[1] = ~((crc >> 8) & 0xFF); + w1_write_block(sl->master, w1_buf, 2); + + /* Wait until busy flag clears (or timeout). */ + if (w1_f19_i2c_busy_wait(sl, count + 1) < 0) + return -ETIMEDOUT; + + /* Read status from DS28E17. */ + w1_read_block(sl->master, w1_buf, 2); + + /* Check error conditions. */ + error = w1_f19_error(sl, w1_buf); + if (error < 0) + return error; + + /* Return number of bytes written. */ + return count; +} + + +/* Write data to I2C slave. */ +static int w1_f19_i2c_write(struct w1_slave *sl, u16 i2c_address, + const u8 *buffer, size_t count, bool stop) +{ + int result; + int remaining = count; + const u8 *p; + u8 command[2]; + + /* Check input. */ + if (count == 0) + return -EOPNOTSUPP; + + /* Check whether we need multiple commands. */ + if (count <= W1_F19_WRITE_DATA_LIMIT) { + /* + * Small data amount. Data can be sent with + * a single onewire command. + */ + + /* Send all data to DS28E17. */ + command[0] = (stop ? W1_F19_WRITE_DATA_WITH_STOP + : W1_F19_WRITE_DATA_NO_STOP); + command[1] = i2c_address << 1; + result = __w1_f19_i2c_write(sl, command, 2, buffer, count); + } else { + /* Large data amount. Data has to be sent in multiple chunks. */ + + /* Send first chunk to DS28E17. */ + p = buffer; + command[0] = W1_F19_WRITE_DATA_NO_STOP; + command[1] = i2c_address << 1; + result = __w1_f19_i2c_write(sl, command, 2, p, + W1_F19_WRITE_DATA_LIMIT); + if (result < 0) + return result; + + /* Resume to same DS28E17. */ + if (w1_reset_resume_command(sl->master)) + return -EIO; + + /* Next data chunk. */ + p += W1_F19_WRITE_DATA_LIMIT; + remaining -= W1_F19_WRITE_DATA_LIMIT; + + while (remaining > W1_F19_WRITE_DATA_LIMIT) { + /* Send intermediate chunk to DS28E17. */ + command[0] = W1_F19_WRITE_DATA_ONLY; + result = __w1_f19_i2c_write(sl, command, 1, p, + W1_F19_WRITE_DATA_LIMIT); + if (result < 0) + return result; + + /* Resume to same DS28E17. */ + if (w1_reset_resume_command(sl->master)) + return -EIO; + + /* Next data chunk. */ + p += W1_F19_WRITE_DATA_LIMIT; + remaining -= W1_F19_WRITE_DATA_LIMIT; + } + + /* Send final chunk to DS28E17. */ + command[0] = (stop ? W1_F19_WRITE_DATA_ONLY_WITH_STOP + : W1_F19_WRITE_DATA_ONLY); + result = __w1_f19_i2c_write(sl, command, 1, p, remaining); + } + + return result; +} + + +/* Read data from I2C slave. */ +static int w1_f19_i2c_read(struct w1_slave *sl, u16 i2c_address, + u8 *buffer, size_t count) +{ + u16 crc; + int error; + u8 w1_buf[5]; + + /* Check input. */ + if (count == 0) + return -EOPNOTSUPP; + + /* Send command to DS28E17. */ + w1_buf[0] = W1_F19_READ_DATA_WITH_STOP; + w1_buf[1] = i2c_address << 1 | 0x01; + w1_buf[2] = count; + crc = crc16(CRC16_INIT, w1_buf, 3); + w1_buf[3] = ~(crc & 0xFF); + w1_buf[4] = ~((crc >> 8) & 0xFF); + w1_write_block(sl->master, w1_buf, 5); + + /* Wait until busy flag clears (or timeout). */ + if (w1_f19_i2c_busy_wait(sl, count + 1) < 0) + return -ETIMEDOUT; + + /* Read status from DS28E17. */ + w1_buf[0] = w1_read_8(sl->master); + w1_buf[1] = 0; + + /* Check error conditions. */ + error = w1_f19_error(sl, w1_buf); + if (error < 0) + return error; + + /* Read received I2C data from DS28E17. */ + return w1_read_block(sl->master, buffer, count); +} + + +/* Write to, then read data from I2C slave. */ +static int w1_f19_i2c_write_read(struct w1_slave *sl, u16 i2c_address, + const u8 *wbuffer, size_t wcount, u8 *rbuffer, size_t rcount) +{ + u16 crc; + int error; + u8 w1_buf[3]; + + /* Check input. */ + if (wcount == 0 || rcount == 0) + return -EOPNOTSUPP; + + /* Send command and I2C data to DS28E17. */ + w1_buf[0] = W1_F19_WRITE_READ_DATA_WITH_STOP; + w1_buf[1] = i2c_address << 1; + w1_buf[2] = wcount; + crc = crc16(CRC16_INIT, w1_buf, 3); + w1_write_block(sl->master, w1_buf, 3); + + crc = crc16(crc, wbuffer, wcount); + w1_write_block(sl->master, wbuffer, wcount); + + w1_buf[0] = rcount; + crc = crc16(crc, w1_buf, 1); + w1_buf[1] = ~(crc & 0xFF); + w1_buf[2] = ~((crc >> 8) & 0xFF); + w1_write_block(sl->master, w1_buf, 3); + + /* Wait until busy flag clears (or timeout). */ + if (w1_f19_i2c_busy_wait(sl, wcount + rcount + 2) < 0) + return -ETIMEDOUT; + + /* Read status from DS28E17. */ + w1_read_block(sl->master, w1_buf, 2); + + /* Check error conditions. */ + error = w1_f19_error(sl, w1_buf); + if (error < 0) + return error; + + /* Read received I2C data from DS28E17. */ + return w1_read_block(sl->master, rbuffer, rcount); +} + + +/* Do an I2C master transfer. */ +static int w1_f19_i2c_master_transfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + struct w1_slave *sl = (struct w1_slave *) adapter->algo_data; + int i = 0; + int result = 0; + + /* Start onewire transaction. */ + mutex_lock(&sl->master->bus_mutex); + + /* Select DS28E17. */ + if (w1_reset_select_slave(sl)) { + i = -EIO; + goto error; + } + + /* Loop while there are still messages to transfer. */ + while (i < num) { + /* + * Check for special case: Small write followed + * by read to same I2C device. + */ + if (i < (num-1) + && msgs[i].addr == msgs[i+1].addr + && !(msgs[i].flags & I2C_M_RD) + && (msgs[i+1].flags & I2C_M_RD) + && (msgs[i].len <= W1_F19_WRITE_DATA_LIMIT)) { + /* + * The DS28E17 has a combined transfer + * for small write+read. + */ + result = w1_f19_i2c_write_read(sl, msgs[i].addr, + msgs[i].buf, msgs[i].len, + msgs[i+1].buf, msgs[i+1].len); + if (result < 0) { + i = result; + goto error; + } + + /* + * Check if we should interpret the read data + * as a length byte. The DS28E17 unfortunately + * has no read without stop, so we can just do + * another simple read in that case. + */ + if (msgs[i+1].flags & I2C_M_RECV_LEN) { + result = w1_f19_i2c_read(sl, msgs[i+1].addr, + &(msgs[i+1].buf[1]), msgs[i+1].buf[0]); + if (result < 0) { + i = result; + goto error; + } + } + + /* Eat up read message, too. */ + i++; + } else if (msgs[i].flags & I2C_M_RD) { + /* Read transfer. */ + result = w1_f19_i2c_read(sl, msgs[i].addr, + msgs[i].buf, msgs[i].len); + if (result < 0) { + i = result; + goto error; + } + + /* + * Check if we should interpret the read data + * as a length byte. The DS28E17 unfortunately + * has no read without stop, so we can just do + * another simple read in that case. + */ + if (msgs[i].flags & I2C_M_RECV_LEN) { + result = w1_f19_i2c_read(sl, + msgs[i].addr, + &(msgs[i].buf[1]), + msgs[i].buf[0]); + if (result < 0) { + i = result; + goto error; + } + } + } else { + /* + * Write transfer. + * Stop condition only for last + * transfer. + */ + result = w1_f19_i2c_write(sl, + msgs[i].addr, + msgs[i].buf, + msgs[i].len, + i == (num-1)); + if (result < 0) { + i = result; + goto error; + } + } + + /* Next message. */ + i++; + + /* Are there still messages to send/receive? */ + if (i < num) { + /* Yes. Resume to same DS28E17. */ + if (w1_reset_resume_command(sl->master)) { + i = -EIO; + goto error; + } + } + } + +error: + /* End onewire transaction. */ + mutex_unlock(&sl->master->bus_mutex); + + /* Return number of messages processed or error. */ + return i; +} + + +/* Get I2C adapter functionality. */ +static u32 w1_f19_i2c_functionality(struct i2c_adapter *adapter) +{ + /* + * Plain I2C functions only. + * SMBus is emulated by the kernel's I2C layer. + * No "I2C_FUNC_SMBUS_QUICK" + * No "I2C_FUNC_SMBUS_READ_BLOCK_DATA" + * No "I2C_FUNC_SMBUS_BLOCK_PROC_CALL" + */ + return I2C_FUNC_I2C | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK | + I2C_FUNC_SMBUS_PEC; +} + + +/* I2C adapter quirks. */ +static const struct i2c_adapter_quirks w1_f19_i2c_adapter_quirks = { + .max_read_len = W1_F19_READ_DATA_LIMIT, +}; + +/* I2C algorithm. */ +static const struct i2c_algorithm w1_f19_i2c_algorithm = { + .master_xfer = w1_f19_i2c_master_transfer, + .functionality = w1_f19_i2c_functionality, +}; + + +/* Read I2C speed from DS28E17. */ +static int w1_f19_get_i2c_speed(struct w1_slave *sl) +{ + struct w1_f19_data *data = sl->family_data; + int result = -EIO; + + /* Start onewire transaction. */ + mutex_lock(&sl->master->bus_mutex); + + /* Select slave. */ + if (w1_reset_select_slave(sl)) + goto error; + + /* Read slave configuration byte. */ + w1_write_8(sl->master, W1_F19_READ_CONFIGURATION); + result = w1_read_8(sl->master); + if (result < 0 || result > 2) { + result = -EIO; + goto error; + } + + /* Update speed in slave specific data. */ + data->speed = result; + +error: + /* End onewire transaction. */ + mutex_unlock(&sl->master->bus_mutex); + + return result; +} + + +/* Set I2C speed on DS28E17. */ +static int __w1_f19_set_i2c_speed(struct w1_slave *sl, u8 speed) +{ + struct w1_f19_data *data = sl->family_data; + const int i2c_speeds[3] = { 100, 400, 900 }; + u8 w1_buf[2]; + + /* Select slave. */ + if (w1_reset_select_slave(sl)) + return -EIO; + + w1_buf[0] = W1_F19_WRITE_CONFIGURATION; + w1_buf[1] = speed; + w1_write_block(sl->master, w1_buf, 2); + + /* Update speed in slave specific data. */ + data->speed = speed; + + dev_info(&sl->dev, "i2c speed set to %d kBaud\n", i2c_speeds[speed]); + + return 0; +} + +static int w1_f19_set_i2c_speed(struct w1_slave *sl, u8 speed) +{ + int result; + + /* Start onewire transaction. */ + mutex_lock(&sl->master->bus_mutex); + + /* Set I2C speed on DS28E17. */ + result = __w1_f19_set_i2c_speed(sl, speed); + + /* End onewire transaction. */ + mutex_unlock(&sl->master->bus_mutex); + + return result; +} + + +/* Sysfs attributes. */ + +/* I2C speed attribute for a single chip. */ +static ssize_t speed_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct w1_slave *sl = dev_to_w1_slave(dev); + int result; + + /* Read current speed from slave. Updates data->speed. */ + result = w1_f19_get_i2c_speed(sl); + if (result < 0) + return result; + + /* Return current speed value. */ + return sprintf(buf, "%d\n", result); +} + +static ssize_t speed_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct w1_slave *sl = dev_to_w1_slave(dev); + int error; + + /* Valid values are: "100", "400", "900" */ + if (count < 3 || count > 4 || !buf) + return -EINVAL; + if (count == 4 && buf[3] != '\n') + return -EINVAL; + if (buf[1] != '0' || buf[2] != '0') + return -EINVAL; + + /* Set speed on slave. */ + switch (buf[0]) { + case '1': + error = w1_f19_set_i2c_speed(sl, 0); + break; + case '4': + error = w1_f19_set_i2c_speed(sl, 1); + break; + case '9': + error = w1_f19_set_i2c_speed(sl, 2); + break; + default: + return -EINVAL; + } + + if (error < 0) + return error; + + /* Return bytes written. */ + return count; +} + +static DEVICE_ATTR_RW(speed); + + +/* Busy stretch attribute for a single chip. */ +static ssize_t stretch_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct w1_slave *sl = dev_to_w1_slave(dev); + struct w1_f19_data *data = sl->family_data; + + /* Return current stretch value. */ + return sprintf(buf, "%d\n", data->stretch); +} + +static ssize_t stretch_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct w1_slave *sl = dev_to_w1_slave(dev); + struct w1_f19_data *data = sl->family_data; + + /* Valid values are '1' to '9' */ + if (count < 1 || count > 2 || !buf) + return -EINVAL; + if (count == 2 && buf[1] != '\n') + return -EINVAL; + if (buf[0] < '1' || buf[0] > '9') + return -EINVAL; + + /* Set busy stretch value. */ + data->stretch = buf[0] & 0x0F; + + /* Return bytes written. */ + return count; +} + +static DEVICE_ATTR_RW(stretch); + + +/* All attributes. */ +static struct attribute *w1_f19_attrs[] = { + &dev_attr_speed.attr, + &dev_attr_stretch.attr, + NULL, +}; + +static const struct attribute_group w1_f19_group = { + .attrs = w1_f19_attrs, +}; + +static const struct attribute_group *w1_f19_groups[] = { + &w1_f19_group, + NULL, +}; + + +/* Slave add and remove functions. */ +static int w1_f19_add_slave(struct w1_slave *sl) +{ + struct w1_f19_data *data = NULL; + + /* Allocate memory for slave specific data. */ + data = devm_kzalloc(&sl->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + sl->family_data = data; + + /* Setup default I2C speed on slave. */ + switch (i2c_speed) { + case 100: + __w1_f19_set_i2c_speed(sl, 0); + break; + case 400: + __w1_f19_set_i2c_speed(sl, 1); + break; + case 900: + __w1_f19_set_i2c_speed(sl, 2); + break; + default: + /* + * A i2c_speed module parameter of anything else + * than 100, 400, 900 means not to touch the + * speed of the DS28E17. + * We assume 400kBaud, the power-on value. + */ + data->speed = 1; + } + + /* + * Setup default busy stretch + * configuration for the DS28E17. + */ + data->stretch = i2c_stretch; + + /* Setup I2C adapter. */ + data->adapter.owner = THIS_MODULE; + data->adapter.algo = &w1_f19_i2c_algorithm; + data->adapter.algo_data = sl; + strcpy(data->adapter.name, "w1-"); + strcat(data->adapter.name, sl->name); + data->adapter.dev.parent = &sl->dev; + data->adapter.quirks = &w1_f19_i2c_adapter_quirks; + + return i2c_add_adapter(&data->adapter); +} + +static void w1_f19_remove_slave(struct w1_slave *sl) +{ + struct w1_f19_data *family_data = sl->family_data; + + /* Delete I2C adapter. */ + i2c_del_adapter(&family_data->adapter); + + /* Free slave specific data. */ + devm_kfree(&sl->dev, family_data); + sl->family_data = NULL; +} + + +/* Declarations within the w1 subsystem. */ +static struct w1_family_ops w1_f19_fops = { + .add_slave = w1_f19_add_slave, + .remove_slave = w1_f19_remove_slave, + .groups = w1_f19_groups, +}; + +static struct w1_family w1_family_19 = { + .fid = W1_FAMILY_DS28E17, + .fops = &w1_f19_fops, +}; + + +/* Module init and remove functions. */ +static int __init w1_f19_init(void) +{ + return w1_register_family(&w1_family_19); +} + +static void __exit w1_f19_fini(void) +{ + w1_unregister_family(&w1_family_19); +} + +module_init(w1_f19_init); +module_exit(w1_f19_fini); + diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index d191e1f80579..075d120e7b88 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -58,7 +58,7 @@ static u8 w1_read_bit(struct w1_master *dev); * @dev: the master device * @bit: 0 - write a 0, 1 - write a 0 read the level */ -static u8 w1_touch_bit(struct w1_master *dev, int bit) +u8 w1_touch_bit(struct w1_master *dev, int bit) { if (dev->bus_master->touch_bit) return dev->bus_master->touch_bit(dev->bus_master->data, bit); @@ -69,6 +69,7 @@ static u8 w1_touch_bit(struct w1_master *dev, int bit) return 0; } } +EXPORT_SYMBOL_GPL(w1_touch_bit); /** * w1_write_bit() - Generates a write-0 or write-1 cycle. |